diff --git a/auth.go b/auth.go index 9cda8b0..010646c 100644 --- a/auth.go +++ b/auth.go @@ -3,7 +3,7 @@ package ssh2docker import ( "fmt" - "golang.org/x/crypto/ssh" + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" ) // ImageIsAllowed returns true if the target image is in the allowed list diff --git a/auth_test.go b/auth_test.go index 7a574e8..481e1d2 100644 --- a/auth_test.go +++ b/auth_test.go @@ -3,7 +3,7 @@ package ssh2docker import ( "testing" - . "github.com/smartystreets/goconvey/convey" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey" ) func TestServer_ImageIsAllowed(t *testing.T) { diff --git a/client.go b/client.go index 0de7dd2..6a9ffa3 100644 --- a/client.go +++ b/client.go @@ -9,9 +9,9 @@ import ( "sync" "syscall" - "github.com/Sirupsen/logrus" - "github.com/kr/pty" - "golang.org/x/crypto/ssh" + "github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus" + "github.com/moul/ssh2docker/vendor/github.com/kr/pty" + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" ) var clientCounter = 0 diff --git a/cmd/ssh2docker/main.go b/cmd/ssh2docker/main.go index 8eedb49..bb4c63c 100644 --- a/cmd/ssh2docker/main.go +++ b/cmd/ssh2docker/main.go @@ -6,9 +6,9 @@ import ( "path" "strings" - "github.com/Sirupsen/logrus" - "github.com/codegangsta/cli" "github.com/moul/ssh2docker" + "github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus" + "github.com/moul/ssh2docker/vendor/github.com/codegangsta/cli" ) var VERSION string diff --git a/keys.go b/keys.go index 2b82db2..2b394aa 100644 --- a/keys.go +++ b/keys.go @@ -5,7 +5,7 @@ import ( "os" "strings" - "golang.org/x/crypto/ssh" + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" ) // AddHostKey parses/loads an ssh key and registers it to the server diff --git a/server.go b/server.go index e04489e..46b8be5 100644 --- a/server.go +++ b/server.go @@ -3,8 +3,8 @@ package ssh2docker import ( "net" - "github.com/Sirupsen/logrus" - "golang.org/x/crypto/ssh" + "github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus" + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" ) // Server is the ssh2docker main structure diff --git a/tty.go b/tty.go index ca42c80..845a619 100644 --- a/tty.go +++ b/tty.go @@ -5,7 +5,7 @@ import ( "syscall" "unsafe" - "github.com/Sirupsen/logrus" + "github.com/moul/ssh2docker/vendor/github.com/Sirupsen/logrus" ) func parseDims(b []byte) (uint32, uint32) { diff --git a/vendor/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md new file mode 100644 index 0000000..b1fe4b6 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/CHANGELOG.md @@ -0,0 +1,41 @@ +# 0.8.6 + +* hooks/raven: allow passing an initialized client + +# 0.8.5 + +* logrus/core: revert #208 + +# 0.8.4 + +* formatter/text: fix data race (#218) + +# 0.8.3 + +* logrus/core: fix entry log level (#208) +* logrus/core: improve performance of text formatter by 40% +* logrus/core: expose `LevelHooks` type +* logrus/core: add support for DragonflyBSD and NetBSD +* formatter/text: print structs more verbosely + +# 0.8.2 + +* logrus: fix more Fatal family functions + +# 0.8.1 + +* logrus: fix not exiting on `Fatalf` and `Fatalln` + +# 0.8.0 + +* logrus: defaults to stderr instead of stdout +* hooks/sentry: add special field for `*http.Request` +* formatter/text: ignore Windows for colors + +# 0.7.3 + +* formatter/\*: allow configuration of timestamp layout + +# 0.7.2 + +* formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/Sirupsen/logrus/LICENSE b/vendor/github.com/Sirupsen/logrus/LICENSE new file mode 100644 index 0000000..f090cb4 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Simon Eskildsen + +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/Sirupsen/logrus/README.md b/vendor/github.com/Sirupsen/logrus/README.md new file mode 100644 index 0000000..9f712a7 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/README.md @@ -0,0 +1,357 @@ +# Logrus :walrus: [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc] + +Logrus is a structured logger for Go (golang), completely API compatible with +the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not +yet stable (pre 1.0). Logrus itself is completely stable and has been used in +many large deployments. The core API is unlikely to change much but please +version control your Logrus to make sure you aren't fetching latest `master` on +every build.** + +Nicely color-coded in development (when a TTY is attached, otherwise just +plain text): + +![Colored](http://i.imgur.com/PY7qMwd.png) + +With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash +or Splunk: + +```json +{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the +ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} + +{"level":"warning","msg":"The group's number increased tremendously!", +"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"A giant walrus appears!", +"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} + +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", +"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} + +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, +"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +``` + +With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not +attached, the output is compatible with the +[logfmt](http://godoc.org/github.com/kr/logfmt) format: + +```text +time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 +time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 +time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true +time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 +time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +exit status 1 +``` + +#### Example + +The simplest way to use Logrus is simply the package-level exported logger: + +```go +package main + +import ( + log "github.com/Sirupsen/logrus" +) + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + }).Info("A walrus appears") +} +``` + +Note that it's completely api-compatible with the stdlib logger, so you can +replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"` +and you'll now have the flexibility of Logrus. You can customize it all you +want: + +```go +package main + +import ( + "os" + log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/airbrake" +) + +func init() { + // Log as JSON instead of the default ASCII formatter. + log.SetFormatter(&log.JSONFormatter{}) + + // Use the Airbrake hook to report errors that have Error severity or above to + // an exception tracker. You can create custom hooks, see the Hooks section. + log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development")) + + // Output to stderr instead of stdout, could also be a file. + log.SetOutput(os.Stderr) + + // Only log the warning severity or above. + log.SetLevel(log.WarnLevel) +} + +func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") + + log.WithFields(log.Fields{ + "omg": true, + "number": 122, + }).Warn("The group's number increased tremendously!") + + log.WithFields(log.Fields{ + "omg": true, + "number": 100, + }).Fatal("The ice breaks!") + + // A common pattern is to re-use fields between logging statements by re-using + // the logrus.Entry returned from WithFields() + contextLogger := log.WithFields(log.Fields{ + "common": "this is a common field", + "other": "I also should be logged always", + }) + + contextLogger.Info("I'll be logged with common and other field") + contextLogger.Info("Me too") +} +``` + +For more advanced usage such as logging to multiple locations from the same +application, you can also create an instance of the `logrus` Logger: + +```go +package main + +import ( + "github.com/Sirupsen/logrus" +) + +// Create a new instance of the logger. You can have any number of instances. +var log = logrus.New() + +func main() { + // The API for setting attributes is a little different than the package level + // exported logger. See Godoc. + log.Out = os.Stderr + + log.WithFields(logrus.Fields{ + "animal": "walrus", + "size": 10, + }).Info("A group of walrus emerges from the ocean") +} +``` + +#### Fields + +Logrus encourages careful, structured logging though logging fields instead of +long, unparseable error messages. For example, instead of: `log.Fatalf("Failed +to send event %s to topic %s with key %d")`, you should log the much more +discoverable: + +```go +log.WithFields(log.Fields{ + "event": event, + "topic": topic, + "key": key, +}).Fatal("Failed to send event") +``` + +We've found this API forces you to think about logging in a way that produces +much more useful logging messages. We've been in countless situations where just +a single added field to a log statement that was already there would've saved us +hours. The `WithFields` call is optional. + +In general, with Logrus using any of the `printf`-family functions should be +seen as a hint you should add a field, however, you can still use the +`printf`-family functions with Logrus. + +#### Hooks + +You can add hooks for logging levels. For example to send errors to an exception +tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to +multiple places simultaneously, e.g. syslog. + +Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in +`init`: + +```go +import ( + log "github.com/Sirupsen/logrus" + "github.com/Sirupsen/logrus/hooks/airbrake" + logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog" + "log/syslog" +) + +func init() { + log.AddHook(airbrake.NewHook("https://example.com", "xyz", "development")) + + hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") + if err != nil { + log.Error("Unable to connect to local syslog daemon") + } else { + log.AddHook(hook) + } +} +``` + + +| Hook | Description | +| ----- | ----------- | +| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | +| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. | +| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. | +| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [Sentry](https://github.com/Sirupsen/logrus/blob/master/hooks/sentry/sentry.go) | Send errors to the Sentry error logging and aggregation service. | +| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | +| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | +| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | +| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) | +| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) | +| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem | +| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger | +| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail | +| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar | +| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd | +| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb | + +#### Level logging + +Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic. + +```go +log.Debug("Useful debugging information.") +log.Info("Something noteworthy happened!") +log.Warn("You should probably take a look at this.") +log.Error("Something failed but I'm not quitting.") +// Calls os.Exit(1) after logging +log.Fatal("Bye.") +// Calls panic() after logging +log.Panic("I'm bailing.") +``` + +You can set the logging level on a `Logger`, then it will only log entries with +that severity or anything above it: + +```go +// Will log anything that is info or above (warn, error, fatal, panic). Default. +log.SetLevel(log.InfoLevel) +``` + +It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose +environment if your application has that. + +#### Entries + +Besides the fields added with `WithField` or `WithFields` some fields are +automatically added to all logging events: + +1. `time`. The timestamp when the entry was created. +2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after + the `AddFields` call. E.g. `Failed to send event.` +3. `level`. The logging level. E.g. `info`. + +#### Environments + +Logrus has no notion of environment. + +If you wish for hooks and formatters to only be used in specific environments, +you should handle that yourself. For example, if your application has a global +variable `Environment`, which is a string representation of the environment you +could do: + +```go +import ( + log "github.com/Sirupsen/logrus" +) + +init() { + // do something here to set environment depending on an environment variable + // or command-line flag + if Environment == "production" { + log.SetFormatter(&log.JSONFormatter{}) + } else { + // The TextFormatter is default, you don't actually have to do this. + log.SetFormatter(&log.TextFormatter{}) + } +} +``` + +This configuration is how `logrus` was intended to be used, but JSON in +production is mostly only useful if you do log aggregation with tools like +Splunk or Logstash. + +#### Formatters + +The built-in logging formatters are: + +* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise + without colors. + * *Note:* to force colored output when there is no TTY, set the `ForceColors` + field to `true`. To force no colored output even if there is a TTY set the + `DisableColors` field to `true` +* `logrus.JSONFormatter`. Logs fields as JSON. +* `logrus_logstash.LogstashFormatter`. Logs fields as Logstash Events (http://logstash.net). + + ```go + logrus.SetFormatter(&logrus_logstash.LogstashFormatter{Type: “application_name"}) + ``` + +Third party logging formatters: + +* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦. + +You can define your formatter by implementing the `Formatter` interface, +requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a +`Fields` type (`map[string]interface{}`) with all your fields as well as the +default ones (see Entries section above): + +```go +type MyJSONFormatter struct { +} + +log.SetFormatter(new(MyJSONFormatter)) + +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + // Note this doesn't include Time, Level and Message which are available on + // the Entry. Consult `godoc` on information about those fields or read the + // source of the official loggers. + serialized, err := json.Marshal(entry.Data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} +``` + +#### Logger as an `io.Writer` + +Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. + +```go +w := logger.Writer() +defer w.Close() + +srv := http.Server{ + // create a stdlib log.Logger that writes to + // logrus.Logger. + ErrorLog: log.New(w, "", 0), +} +``` + +Each line written to that writer will be printed the usual way, using formatters +and hooks. The level for those entries is `info`. + +#### Rotation + +Log rotation is not provided with Logrus. Log rotation should be done by an +external program (like `logrotate(8)`) that can compress and delete old log +entries. It should not be a feature of the application-level logger. + + +[godoc]: https://godoc.org/github.com/Sirupsen/logrus diff --git a/vendor/github.com/Sirupsen/logrus/entry.go b/vendor/github.com/Sirupsen/logrus/entry.go new file mode 100644 index 0000000..04673a0 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/entry.go @@ -0,0 +1,256 @@ +package logrus + +import ( + "bytes" + "fmt" + "io" + "os" + "time" +) + +// An entry is the final or intermediate Logrus logging entry. It contains all +// the fields passed with WithField{,s}. It's finally logged when Debug, Info, +// Warn, Error, Fatal or Panic is called on it. These objects can be reused and +// passed around as much as you wish to avoid field duplication. +type Entry struct { + Logger *Logger + + // Contains all the fields set by the user. + Data Fields + + // Time at which the log entry was created + Time time.Time + + // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic + Level Level + + // Message passed to Debug, Info, Warn, Error, Fatal or Panic + Message string +} + +func NewEntry(logger *Logger) *Entry { + return &Entry{ + Logger: logger, + // Default is three fields, give a little extra room + Data: make(Fields, 5), + } +} + +// Returns a reader for the entry, which is a proxy to the formatter. +func (entry *Entry) Reader() (*bytes.Buffer, error) { + serialized, err := entry.Logger.Formatter.Format(entry) + return bytes.NewBuffer(serialized), err +} + +// Returns the string representation from the reader and ultimately the +// formatter. +func (entry *Entry) String() (string, error) { + reader, err := entry.Reader() + if err != nil { + return "", err + } + + return reader.String(), err +} + +// Add a single field to the Entry. +func (entry *Entry) WithField(key string, value interface{}) *Entry { + return entry.WithFields(Fields{key: value}) +} + +// Add a map of fields to the Entry. +func (entry *Entry) WithFields(fields Fields) *Entry { + data := Fields{} + for k, v := range entry.Data { + data[k] = v + } + for k, v := range fields { + data[k] = v + } + return &Entry{Logger: entry.Logger, Data: data} +} + +// This function is not declared with a pointer value because otherwise +// race conditions will occur when using multiple goroutines +func (entry Entry) log(level Level, msg string) { + entry.Time = time.Now() + entry.Level = level + entry.Message = msg + + if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) + entry.Logger.mu.Unlock() + } + + reader, err := entry.Reader() + if err != nil { + entry.Logger.mu.Lock() + fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + entry.Logger.mu.Unlock() + } + + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() + + _, err = io.Copy(entry.Logger.Out, reader) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + } + + // To avoid Entry#log() returning a value that only would make sense for + // panic() to use in Entry#Panic(), we avoid the allocation by checking + // directly here. + if level <= PanicLevel { + panic(&entry) + } +} + +func (entry *Entry) Debug(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.log(DebugLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Print(args ...interface{}) { + entry.Info(args...) +} + +func (entry *Entry) Info(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.log(InfoLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warn(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.log(WarnLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Warning(args ...interface{}) { + entry.Warn(args...) +} + +func (entry *Entry) Error(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.log(ErrorLevel, fmt.Sprint(args...)) + } +} + +func (entry *Entry) Fatal(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.log(FatalLevel, fmt.Sprint(args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panic(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.log(PanicLevel, fmt.Sprint(args...)) + } + panic(fmt.Sprint(args...)) +} + +// Entry Printf family functions + +func (entry *Entry) Debugf(format string, args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Infof(format string, args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Printf(format string, args ...interface{}) { + entry.Infof(format, args...) +} + +func (entry *Entry) Warnf(format string, args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Warningf(format string, args ...interface{}) { + entry.Warnf(format, args...) +} + +func (entry *Entry) Errorf(format string, args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(fmt.Sprintf(format, args...)) + } +} + +func (entry *Entry) Fatalf(format string, args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(fmt.Sprintf(format, args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panicf(format string, args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(fmt.Sprintf(format, args...)) + } +} + +// Entry Println family functions + +func (entry *Entry) Debugln(args ...interface{}) { + if entry.Logger.Level >= DebugLevel { + entry.Debug(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Infoln(args ...interface{}) { + if entry.Logger.Level >= InfoLevel { + entry.Info(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Println(args ...interface{}) { + entry.Infoln(args...) +} + +func (entry *Entry) Warnln(args ...interface{}) { + if entry.Logger.Level >= WarnLevel { + entry.Warn(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Warningln(args ...interface{}) { + entry.Warnln(args...) +} + +func (entry *Entry) Errorln(args ...interface{}) { + if entry.Logger.Level >= ErrorLevel { + entry.Error(entry.sprintlnn(args...)) + } +} + +func (entry *Entry) Fatalln(args ...interface{}) { + if entry.Logger.Level >= FatalLevel { + entry.Fatal(entry.sprintlnn(args...)) + } + os.Exit(1) +} + +func (entry *Entry) Panicln(args ...interface{}) { + if entry.Logger.Level >= PanicLevel { + entry.Panic(entry.sprintlnn(args...)) + } +} + +// Sprintlnn => Sprint no newline. This is to get the behavior of how +// fmt.Sprintln where spaces are always added between operands, regardless of +// their type. Instead of vendoring the Sprintln implementation to spare a +// string allocation, we do the simplest thing. +func (entry *Entry) sprintlnn(args ...interface{}) string { + msg := fmt.Sprintln(args...) + return msg[:len(msg)-1] +} diff --git a/vendor/github.com/Sirupsen/logrus/entry_test.go b/vendor/github.com/Sirupsen/logrus/entry_test.go new file mode 100644 index 0000000..ed4054f --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/entry_test.go @@ -0,0 +1,53 @@ +package logrus + +import ( + "bytes" + "fmt" + "testing" + + "github.com/moul/ssh2docker/vendor/github.com/stretchr/testify/assert" +) + +func TestEntryPanicln(t *testing.T) { + errBoom := fmt.Errorf("boom time") + + defer func() { + p := recover() + assert.NotNil(t, p) + + switch pVal := p.(type) { + case *Entry: + assert.Equal(t, "kaboom", pVal.Message) + assert.Equal(t, errBoom, pVal.Data["err"]) + default: + t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) + } + }() + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + entry.WithField("err", errBoom).Panicln("kaboom") +} + +func TestEntryPanicf(t *testing.T) { + errBoom := fmt.Errorf("boom again") + + defer func() { + p := recover() + assert.NotNil(t, p) + + switch pVal := p.(type) { + case *Entry: + assert.Equal(t, "kaboom true", pVal.Message) + assert.Equal(t, errBoom, pVal.Data["err"]) + default: + t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) + } + }() + + logger := New() + logger.Out = &bytes.Buffer{} + entry := NewEntry(logger) + entry.WithField("err", errBoom).Panicf("kaboom %v", true) +} diff --git a/vendor/github.com/Sirupsen/logrus/exported.go b/vendor/github.com/Sirupsen/logrus/exported.go new file mode 100644 index 0000000..a67e1b8 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/exported.go @@ -0,0 +1,188 @@ +package logrus + +import ( + "io" +) + +var ( + // std is the name of the standard logger in stdlib `log` + std = New() +) + +func StandardLogger() *Logger { + return std +} + +// SetOutput sets the standard logger output. +func SetOutput(out io.Writer) { + std.mu.Lock() + defer std.mu.Unlock() + std.Out = out +} + +// SetFormatter sets the standard logger formatter. +func SetFormatter(formatter Formatter) { + std.mu.Lock() + defer std.mu.Unlock() + std.Formatter = formatter +} + +// SetLevel sets the standard logger level. +func SetLevel(level Level) { + std.mu.Lock() + defer std.mu.Unlock() + std.Level = level +} + +// GetLevel returns the standard logger level. +func GetLevel() Level { + std.mu.Lock() + defer std.mu.Unlock() + return std.Level +} + +// AddHook adds a hook to the standard logger hooks. +func AddHook(hook Hook) { + std.mu.Lock() + defer std.mu.Unlock() + std.Hooks.Add(hook) +} + +// WithField creates an entry from the standard logger and adds a field to +// it. If you want multiple fields, use `WithFields`. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithField(key string, value interface{}) *Entry { + return std.WithField(key, value) +} + +// WithFields creates an entry from the standard logger and adds multiple +// fields to it. This is simply a helper for `WithField`, invoking it +// once for each field. +// +// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal +// or Panic on the Entry it returns. +func WithFields(fields Fields) *Entry { + return std.WithFields(fields) +} + +// Debug logs a message at level Debug on the standard logger. +func Debug(args ...interface{}) { + std.Debug(args...) +} + +// Print logs a message at level Info on the standard logger. +func Print(args ...interface{}) { + std.Print(args...) +} + +// Info logs a message at level Info on the standard logger. +func Info(args ...interface{}) { + std.Info(args...) +} + +// Warn logs a message at level Warn on the standard logger. +func Warn(args ...interface{}) { + std.Warn(args...) +} + +// Warning logs a message at level Warn on the standard logger. +func Warning(args ...interface{}) { + std.Warning(args...) +} + +// Error logs a message at level Error on the standard logger. +func Error(args ...interface{}) { + std.Error(args...) +} + +// Panic logs a message at level Panic on the standard logger. +func Panic(args ...interface{}) { + std.Panic(args...) +} + +// Fatal logs a message at level Fatal on the standard logger. +func Fatal(args ...interface{}) { + std.Fatal(args...) +} + +// Debugf logs a message at level Debug on the standard logger. +func Debugf(format string, args ...interface{}) { + std.Debugf(format, args...) +} + +// Printf logs a message at level Info on the standard logger. +func Printf(format string, args ...interface{}) { + std.Printf(format, args...) +} + +// Infof logs a message at level Info on the standard logger. +func Infof(format string, args ...interface{}) { + std.Infof(format, args...) +} + +// Warnf logs a message at level Warn on the standard logger. +func Warnf(format string, args ...interface{}) { + std.Warnf(format, args...) +} + +// Warningf logs a message at level Warn on the standard logger. +func Warningf(format string, args ...interface{}) { + std.Warningf(format, args...) +} + +// Errorf logs a message at level Error on the standard logger. +func Errorf(format string, args ...interface{}) { + std.Errorf(format, args...) +} + +// Panicf logs a message at level Panic on the standard logger. +func Panicf(format string, args ...interface{}) { + std.Panicf(format, args...) +} + +// Fatalf logs a message at level Fatal on the standard logger. +func Fatalf(format string, args ...interface{}) { + std.Fatalf(format, args...) +} + +// Debugln logs a message at level Debug on the standard logger. +func Debugln(args ...interface{}) { + std.Debugln(args...) +} + +// Println logs a message at level Info on the standard logger. +func Println(args ...interface{}) { + std.Println(args...) +} + +// Infoln logs a message at level Info on the standard logger. +func Infoln(args ...interface{}) { + std.Infoln(args...) +} + +// Warnln logs a message at level Warn on the standard logger. +func Warnln(args ...interface{}) { + std.Warnln(args...) +} + +// Warningln logs a message at level Warn on the standard logger. +func Warningln(args ...interface{}) { + std.Warningln(args...) +} + +// Errorln logs a message at level Error on the standard logger. +func Errorln(args ...interface{}) { + std.Errorln(args...) +} + +// Panicln logs a message at level Panic on the standard logger. +func Panicln(args ...interface{}) { + std.Panicln(args...) +} + +// Fatalln logs a message at level Fatal on the standard logger. +func Fatalln(args ...interface{}) { + std.Fatalln(args...) +} diff --git a/vendor/github.com/Sirupsen/logrus/formatter.go b/vendor/github.com/Sirupsen/logrus/formatter.go new file mode 100644 index 0000000..104d689 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/formatter.go @@ -0,0 +1,48 @@ +package logrus + +import "time" + +const DefaultTimestampFormat = time.RFC3339 + +// The Formatter interface is used to implement a custom Formatter. It takes an +// `Entry`. It exposes all the fields, including the default ones: +// +// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. +// * `entry.Data["time"]`. The timestamp. +// * `entry.Data["level"]. The level the entry was logged at. +// +// Any additional fields added with `WithField` or `WithFields` are also in +// `entry.Data`. Format is expected to return an array of bytes which are then +// logged to `logger.Out`. +type Formatter interface { + Format(*Entry) ([]byte, error) +} + +// This is to not silently overwrite `time`, `msg` and `level` fields when +// dumping it. If this code wasn't there doing: +// +// logrus.WithField("level", 1).Info("hello") +// +// Would just silently drop the user provided level. Instead with this code +// it'll logged as: +// +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// +// It's not exported because it's still using Data in an opinionated way. It's to +// avoid code duplication between the two default formatters. +func prefixFieldClashes(data Fields) { + _, ok := data["time"] + if ok { + data["fields.time"] = data["time"] + } + + _, ok = data["msg"] + if ok { + data["fields.msg"] = data["msg"] + } + + _, ok = data["level"] + if ok { + data["fields.level"] = data["level"] + } +} diff --git a/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go new file mode 100644 index 0000000..c6d290c --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/formatter_bench_test.go @@ -0,0 +1,98 @@ +package logrus + +import ( + "fmt" + "testing" + "time" +) + +// smallFields is a small size data set for benchmarking +var smallFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", +} + +// largeFields is a large size data set for benchmarking +var largeFields = Fields{ + "foo": "bar", + "baz": "qux", + "one": "two", + "three": "four", + "five": "six", + "seven": "eight", + "nine": "ten", + "eleven": "twelve", + "thirteen": "fourteen", + "fifteen": "sixteen", + "seventeen": "eighteen", + "nineteen": "twenty", + "a": "b", + "c": "d", + "e": "f", + "g": "h", + "i": "j", + "k": "l", + "m": "n", + "o": "p", + "q": "r", + "s": "t", + "u": "v", + "w": "x", + "y": "z", + "this": "will", + "make": "thirty", + "entries": "yeah", +} + +var errorFields = Fields{ + "foo": fmt.Errorf("bar"), + "baz": fmt.Errorf("qux"), +} + +func BenchmarkErrorTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields) +} + +func BenchmarkSmallTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) +} + +func BenchmarkLargeTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) +} + +func BenchmarkSmallColoredTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) +} + +func BenchmarkLargeColoredTextFormatter(b *testing.B) { + doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) +} + +func BenchmarkSmallJSONFormatter(b *testing.B) { + doBenchmark(b, &JSONFormatter{}, smallFields) +} + +func BenchmarkLargeJSONFormatter(b *testing.B) { + doBenchmark(b, &JSONFormatter{}, largeFields) +} + +func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { + entry := &Entry{ + Time: time.Time{}, + Level: InfoLevel, + Message: "message", + Data: fields, + } + var d []byte + var err error + for i := 0; i < b.N; i++ { + d, err = formatter.Format(entry) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(len(d))) + } +} diff --git a/vendor/github.com/Sirupsen/logrus/hook_test.go b/vendor/github.com/Sirupsen/logrus/hook_test.go new file mode 100644 index 0000000..37027b6 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/hook_test.go @@ -0,0 +1,122 @@ +package logrus + +import ( + "testing" + + "github.com/moul/ssh2docker/vendor/github.com/stretchr/testify/assert" +) + +type TestHook struct { + Fired bool +} + +func (hook *TestHook) Fire(entry *Entry) error { + hook.Fired = true + return nil +} + +func (hook *TestHook) Levels() []Level { + return []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + PanicLevel, + } +} + +func TestHookFires(t *testing.T) { + hook := new(TestHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + assert.Equal(t, hook.Fired, false) + + log.Print("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} + +type ModifyHook struct { +} + +func (hook *ModifyHook) Fire(entry *Entry) error { + entry.Data["wow"] = "whale" + return nil +} + +func (hook *ModifyHook) Levels() []Level { + return []Level{ + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + PanicLevel, + } +} + +func TestHookCanModifyEntry(t *testing.T) { + hook := new(ModifyHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.WithField("wow", "elephant").Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["wow"], "whale") + }) +} + +func TestCanFireMultipleHooks(t *testing.T) { + hook1 := new(ModifyHook) + hook2 := new(TestHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook1) + log.Hooks.Add(hook2) + + log.WithField("wow", "elephant").Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["wow"], "whale") + assert.Equal(t, hook2.Fired, true) + }) +} + +type ErrorHook struct { + Fired bool +} + +func (hook *ErrorHook) Fire(entry *Entry) error { + hook.Fired = true + return nil +} + +func (hook *ErrorHook) Levels() []Level { + return []Level{ + ErrorLevel, + } +} + +func TestErrorHookShouldntFireOnInfo(t *testing.T) { + hook := new(ErrorHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.Info("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, false) + }) +} + +func TestErrorHookShouldFireOnError(t *testing.T) { + hook := new(ErrorHook) + + LogAndAssertJSON(t, func(log *Logger) { + log.Hooks.Add(hook) + log.Error("test") + }, func(fields Fields) { + assert.Equal(t, hook.Fired, true) + }) +} diff --git a/vendor/github.com/Sirupsen/logrus/hooks.go b/vendor/github.com/Sirupsen/logrus/hooks.go new file mode 100644 index 0000000..3f151cd --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/hooks.go @@ -0,0 +1,34 @@ +package logrus + +// A hook to be fired when logging on the logging levels returned from +// `Levels()` on your implementation of the interface. Note that this is not +// fired in a goroutine or a channel with workers, you should handle such +// functionality yourself if your call is non-blocking and you don't wish for +// the logging calls for levels returned from `Levels()` to block. +type Hook interface { + Levels() []Level + Fire(*Entry) error +} + +// Internal type for storing the hooks on a logger instance. +type LevelHooks map[Level][]Hook + +// Add a hook to an instance of logger. This is called with +// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. +func (hooks LevelHooks) Add(hook Hook) { + for _, level := range hook.Levels() { + hooks[level] = append(hooks[level], hook) + } +} + +// Fire all the hooks for the passed level. Used by `entry.log` to fire +// appropriate hooks for a log entry. +func (hooks LevelHooks) Fire(level Level, entry *Entry) error { + for _, hook := range hooks[level] { + if err := hook.Fire(entry); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter.go b/vendor/github.com/Sirupsen/logrus/json_formatter.go new file mode 100644 index 0000000..2ad6dc5 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/json_formatter.go @@ -0,0 +1,41 @@ +package logrus + +import ( + "encoding/json" + "fmt" +) + +type JSONFormatter struct { + // TimestampFormat sets the format used for marshaling timestamps. + TimestampFormat string +} + +func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { + data := make(Fields, len(entry.Data)+3) + for k, v := range entry.Data { + switch v := v.(type) { + case error: + // Otherwise errors are ignored by `encoding/json` + // https://github.com/Sirupsen/logrus/issues/137 + data[k] = v.Error() + default: + data[k] = v + } + } + prefixFieldClashes(data) + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat + } + + data["time"] = entry.Time.Format(timestampFormat) + data["msg"] = entry.Message + data["level"] = entry.Level.String() + + serialized, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) + } + return append(serialized, '\n'), nil +} diff --git a/vendor/github.com/Sirupsen/logrus/json_formatter_test.go b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go new file mode 100644 index 0000000..1d70873 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/json_formatter_test.go @@ -0,0 +1,120 @@ +package logrus + +import ( + "encoding/json" + "errors" + + "testing" +) + +func TestErrorNotLost(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("error", errors.New("wild walrus"))) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["error"] != "wild walrus" { + t.Fatal("Error field not set") + } +} + +func TestErrorNotLostOnFieldNotNamedError(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("omg", errors.New("wild walrus"))) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["omg"] != "wild walrus" { + t.Fatal("Error field not set") + } +} + +func TestFieldClashWithTime(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("time", "right now!")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.time"] != "right now!" { + t.Fatal("fields.time not set to original time field") + } + + if entry["time"] != "0001-01-01T00:00:00Z" { + t.Fatal("time field not set to current time, was: ", entry["time"]) + } +} + +func TestFieldClashWithMsg(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("msg", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.msg"] != "something" { + t.Fatal("fields.msg not set to original msg field") + } +} + +func TestFieldClashWithLevel(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + entry := make(map[string]interface{}) + err = json.Unmarshal(b, &entry) + if err != nil { + t.Fatal("Unable to unmarshal formatted entry: ", err) + } + + if entry["fields.level"] != "something" { + t.Fatal("fields.level not set to original level field") + } +} + +func TestJSONEntryEndsWithNewline(t *testing.T) { + formatter := &JSONFormatter{} + + b, err := formatter.Format(WithField("level", "something")) + if err != nil { + t.Fatal("Unable to format entry: ", err) + } + + if b[len(b)-1] != '\n' { + t.Fatal("Expected JSON log entry to end with a newline") + } +} diff --git a/vendor/github.com/Sirupsen/logrus/logger.go b/vendor/github.com/Sirupsen/logrus/logger.go new file mode 100644 index 0000000..fd9804c --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/logger.go @@ -0,0 +1,206 @@ +package logrus + +import ( + "io" + "os" + "sync" +) + +type Logger struct { + // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a + // file, or leave it default which is `os.Stderr`. You can also set this to + // something more adventorous, such as logging to Kafka. + Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging + // levels and log entries. For example, to send errors to an error tracking + // service, log to StatsD or dump the core on fatal errors. + Hooks LevelHooks + // All log entries pass through the formatter before logged to Out. The + // included formatters are `TextFormatter` and `JSONFormatter` for which + // TextFormatter is the default. In development (when a TTY is attached) it + // logs with colors, but to a file it wouldn't. You can easily implement your + // own that implements the `Formatter` interface, see the `README` or included + // formatters for examples. + Formatter Formatter + // The logging level the logger should log at. This is typically (and defaults + // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be + // logged. `logrus.Debug` is useful in + Level Level + // Used to sync writing to the log. + mu sync.Mutex +} + +// Creates a new logger. Configuration should be set by changing `Formatter`, +// `Out` and `Hooks` directly on the default logger instance. You can also just +// instantiate your own: +// +// var log = &Logger{ +// Out: os.Stderr, +// Formatter: new(JSONFormatter), +// Hooks: make(LevelHooks), +// Level: logrus.DebugLevel, +// } +// +// It's recommended to make this a global instance called `log`. +func New() *Logger { + return &Logger{ + Out: os.Stderr, + Formatter: new(TextFormatter), + Hooks: make(LevelHooks), + Level: InfoLevel, + } +} + +// Adds a field to the log entry, note that you it doesn't log until you call +// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. +// If you want multiple fields, use `WithFields`. +func (logger *Logger) WithField(key string, value interface{}) *Entry { + return NewEntry(logger).WithField(key, value) +} + +// Adds a struct of fields to the log entry. All it does is call `WithField` for +// each `Field`. +func (logger *Logger) WithFields(fields Fields) *Entry { + return NewEntry(logger).WithFields(fields) +} + +func (logger *Logger) Debugf(format string, args ...interface{}) { + if logger.Level >= DebugLevel { + NewEntry(logger).Debugf(format, args...) + } +} + +func (logger *Logger) Infof(format string, args ...interface{}) { + if logger.Level >= InfoLevel { + NewEntry(logger).Infof(format, args...) + } +} + +func (logger *Logger) Printf(format string, args ...interface{}) { + NewEntry(logger).Printf(format, args...) +} + +func (logger *Logger) Warnf(format string, args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnf(format, args...) + } +} + +func (logger *Logger) Warningf(format string, args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnf(format, args...) + } +} + +func (logger *Logger) Errorf(format string, args ...interface{}) { + if logger.Level >= ErrorLevel { + NewEntry(logger).Errorf(format, args...) + } +} + +func (logger *Logger) Fatalf(format string, args ...interface{}) { + if logger.Level >= FatalLevel { + NewEntry(logger).Fatalf(format, args...) + } + os.Exit(1) +} + +func (logger *Logger) Panicf(format string, args ...interface{}) { + if logger.Level >= PanicLevel { + NewEntry(logger).Panicf(format, args...) + } +} + +func (logger *Logger) Debug(args ...interface{}) { + if logger.Level >= DebugLevel { + NewEntry(logger).Debug(args...) + } +} + +func (logger *Logger) Info(args ...interface{}) { + if logger.Level >= InfoLevel { + NewEntry(logger).Info(args...) + } +} + +func (logger *Logger) Print(args ...interface{}) { + NewEntry(logger).Info(args...) +} + +func (logger *Logger) Warn(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warn(args...) + } +} + +func (logger *Logger) Warning(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warn(args...) + } +} + +func (logger *Logger) Error(args ...interface{}) { + if logger.Level >= ErrorLevel { + NewEntry(logger).Error(args...) + } +} + +func (logger *Logger) Fatal(args ...interface{}) { + if logger.Level >= FatalLevel { + NewEntry(logger).Fatal(args...) + } + os.Exit(1) +} + +func (logger *Logger) Panic(args ...interface{}) { + if logger.Level >= PanicLevel { + NewEntry(logger).Panic(args...) + } +} + +func (logger *Logger) Debugln(args ...interface{}) { + if logger.Level >= DebugLevel { + NewEntry(logger).Debugln(args...) + } +} + +func (logger *Logger) Infoln(args ...interface{}) { + if logger.Level >= InfoLevel { + NewEntry(logger).Infoln(args...) + } +} + +func (logger *Logger) Println(args ...interface{}) { + NewEntry(logger).Println(args...) +} + +func (logger *Logger) Warnln(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnln(args...) + } +} + +func (logger *Logger) Warningln(args ...interface{}) { + if logger.Level >= WarnLevel { + NewEntry(logger).Warnln(args...) + } +} + +func (logger *Logger) Errorln(args ...interface{}) { + if logger.Level >= ErrorLevel { + NewEntry(logger).Errorln(args...) + } +} + +func (logger *Logger) Fatalln(args ...interface{}) { + if logger.Level >= FatalLevel { + NewEntry(logger).Fatalln(args...) + } + os.Exit(1) +} + +func (logger *Logger) Panicln(args ...interface{}) { + if logger.Level >= PanicLevel { + NewEntry(logger).Panicln(args...) + } +} diff --git a/vendor/github.com/Sirupsen/logrus/logrus.go b/vendor/github.com/Sirupsen/logrus/logrus.go new file mode 100644 index 0000000..43ee12e --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/logrus.go @@ -0,0 +1,94 @@ +package logrus + +import ( + "fmt" + "log" +) + +// Fields type, used to pass to `WithFields`. +type Fields map[string]interface{} + +// Level type +type Level uint8 + +// Convert the Level to a string. E.g. PanicLevel becomes "panic". +func (level Level) String() string { + switch level { + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warning" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + case PanicLevel: + return "panic" + } + + return "unknown" +} + +// ParseLevel takes a string level and returns the Logrus log level constant. +func ParseLevel(lvl string) (Level, error) { + switch lvl { + case "panic": + return PanicLevel, nil + case "fatal": + return FatalLevel, nil + case "error": + return ErrorLevel, nil + case "warn", "warning": + return WarnLevel, nil + case "info": + return InfoLevel, nil + case "debug": + return DebugLevel, nil + } + + var l Level + return l, fmt.Errorf("not a valid logrus Level: %q", lvl) +} + +// These are the different logging levels. You can set the logging level to log +// on your instance of logger, obtained with `logrus.New()`. +const ( + // PanicLevel level, highest level of severity. Logs and then calls panic with the + // message passed to Debug, Info, ... + PanicLevel Level = iota + // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the + // logging level is set to Panic. + FatalLevel + // ErrorLevel level. Logs. Used for errors that should definitely be noted. + // Commonly used for hooks to send errors to an error tracking service. + ErrorLevel + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel + // InfoLevel level. General operational entries about what's going on inside the + // application. + InfoLevel + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel +) + +// Won't compile if StdLogger can't be realized by a log.Logger +var _ StdLogger = &log.Logger{} + +// StdLogger is what your logrus-enabled library should take, that way +// it'll accept a stdlib logger and a logrus logger. There's no standard +// interface, this is the closest we get, unfortunately. +type StdLogger interface { + Print(...interface{}) + Printf(string, ...interface{}) + Println(...interface{}) + + Fatal(...interface{}) + Fatalf(string, ...interface{}) + Fatalln(...interface{}) + + Panic(...interface{}) + Panicf(string, ...interface{}) + Panicln(...interface{}) +} diff --git a/vendor/github.com/Sirupsen/logrus/logrus_test.go b/vendor/github.com/Sirupsen/logrus/logrus_test.go new file mode 100644 index 0000000..5719a3c --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/logrus_test.go @@ -0,0 +1,301 @@ +package logrus + +import ( + "bytes" + "encoding/json" + "strconv" + "strings" + "sync" + "testing" + + "github.com/moul/ssh2docker/vendor/github.com/stretchr/testify/assert" +) + +func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + log(logger) + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + assertions(fields) +} + +func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { + var buffer bytes.Buffer + + logger := New() + logger.Out = &buffer + logger.Formatter = &TextFormatter{ + DisableColors: true, + } + + log(logger) + + fields := make(map[string]string) + for _, kv := range strings.Split(buffer.String(), " ") { + if !strings.Contains(kv, "=") { + continue + } + kvArr := strings.Split(kv, "=") + key := strings.TrimSpace(kvArr[0]) + val := kvArr[1] + if kvArr[1][0] == '"' { + var err error + val, err = strconv.Unquote(val) + assert.NoError(t, err) + } + fields[key] = val + } + assertions(fields) +} + +func TestPrint(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Print("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "info") + }) +} + +func TestInfo(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "info") + }) +} + +func TestWarn(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Warn("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["level"], "warning") + }) +} + +func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln("test", "test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test test") + }) +} + +func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln("test", 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test 10") + }) +} + +func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln(10, 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "10 10") + }) +} + +func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Infoln(10, 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "10 10") + }) +} + +func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test", 10) + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test10") + }) +} + +func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.Info("test", "test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "testtest") + }) +} + +func TestWithFieldsShouldAllowAssignments(t *testing.T) { + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + localLog := logger.WithFields(Fields{ + "key1": "value1", + }) + + localLog.WithField("key2", "value2").Info("test") + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + assert.Equal(t, "value2", fields["key2"]) + assert.Equal(t, "value1", fields["key1"]) + + buffer = bytes.Buffer{} + fields = Fields{} + localLog.Info("test") + err = json.Unmarshal(buffer.Bytes(), &fields) + assert.Nil(t, err) + + _, ok := fields["key2"] + assert.Equal(t, false, ok) + assert.Equal(t, "value1", fields["key1"]) +} + +func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("msg", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + }) +} + +func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("msg", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["msg"], "test") + assert.Equal(t, fields["fields.msg"], "hello") + }) +} + +func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("time", "hello").Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["fields.time"], "hello") + }) +} + +func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { + LogAndAssertJSON(t, func(log *Logger) { + log.WithField("level", 1).Info("test") + }, func(fields Fields) { + assert.Equal(t, fields["level"], "info") + assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only + }) +} + +func TestDefaultFieldsAreNotPrefixed(t *testing.T) { + LogAndAssertText(t, func(log *Logger) { + ll := log.WithField("herp", "derp") + ll.Info("hello") + ll.Info("bye") + }, func(fields map[string]string) { + for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} { + if _, ok := fields[fieldName]; ok { + t.Fatalf("should not have prefixed %q: %v", fieldName, fields) + } + } + }) +} + +func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { + + var buffer bytes.Buffer + var fields Fields + + logger := New() + logger.Out = &buffer + logger.Formatter = new(JSONFormatter) + + llog := logger.WithField("context", "eating raw fish") + + llog.Info("looks delicious") + + err := json.Unmarshal(buffer.Bytes(), &fields) + assert.NoError(t, err, "should have decoded first message") + assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") + assert.Equal(t, fields["msg"], "looks delicious") + assert.Equal(t, fields["context"], "eating raw fish") + + buffer.Reset() + + llog.Warn("omg it is!") + + err = json.Unmarshal(buffer.Bytes(), &fields) + assert.NoError(t, err, "should have decoded second message") + assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") + assert.Equal(t, fields["msg"], "omg it is!") + assert.Equal(t, fields["context"], "eating raw fish") + assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") + +} + +func TestConvertLevelToString(t *testing.T) { + assert.Equal(t, "debug", DebugLevel.String()) + assert.Equal(t, "info", InfoLevel.String()) + assert.Equal(t, "warning", WarnLevel.String()) + assert.Equal(t, "error", ErrorLevel.String()) + assert.Equal(t, "fatal", FatalLevel.String()) + assert.Equal(t, "panic", PanicLevel.String()) +} + +func TestParseLevel(t *testing.T) { + l, err := ParseLevel("panic") + assert.Nil(t, err) + assert.Equal(t, PanicLevel, l) + + l, err = ParseLevel("fatal") + assert.Nil(t, err) + assert.Equal(t, FatalLevel, l) + + l, err = ParseLevel("error") + assert.Nil(t, err) + assert.Equal(t, ErrorLevel, l) + + l, err = ParseLevel("warn") + assert.Nil(t, err) + assert.Equal(t, WarnLevel, l) + + l, err = ParseLevel("warning") + assert.Nil(t, err) + assert.Equal(t, WarnLevel, l) + + l, err = ParseLevel("info") + assert.Nil(t, err) + assert.Equal(t, InfoLevel, l) + + l, err = ParseLevel("debug") + assert.Nil(t, err) + assert.Equal(t, DebugLevel, l) + + l, err = ParseLevel("invalid") + assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) +} + +func TestGetSetLevelRace(t *testing.T) { + wg := sync.WaitGroup{} + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + if i%2 == 0 { + SetLevel(InfoLevel) + } else { + GetLevel() + } + }(i) + + } + wg.Wait() +} diff --git a/vendor/github.com/Sirupsen/logrus/terminal_bsd.go b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go new file mode 100644 index 0000000..71f8d67 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_bsd.go @@ -0,0 +1,9 @@ +// +build darwin freebsd openbsd netbsd dragonfly + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA + +type Termios syscall.Termios diff --git a/vendor/github.com/Sirupsen/logrus/terminal_linux.go b/vendor/github.com/Sirupsen/logrus/terminal_linux.go new file mode 100644 index 0000000..a2c0b40 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_linux.go @@ -0,0 +1,12 @@ +// Based on ssh/terminal: +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package logrus + +import "syscall" + +const ioctlReadTermios = syscall.TCGETS + +type Termios syscall.Termios diff --git a/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go new file mode 100644 index 0000000..4bb5376 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_notwindows.go @@ -0,0 +1,21 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux darwin freebsd openbsd netbsd dragonfly + +package logrus + +import ( + "syscall" + "unsafe" +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal() bool { + fd := syscall.Stdout + var termios Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} diff --git a/vendor/github.com/Sirupsen/logrus/terminal_windows.go b/vendor/github.com/Sirupsen/logrus/terminal_windows.go new file mode 100644 index 0000000..2e09f6f --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/terminal_windows.go @@ -0,0 +1,27 @@ +// Based on ssh/terminal: +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package logrus + +import ( + "syscall" + "unsafe" +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") +) + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal() bool { + fd := syscall.Stdout + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter.go b/vendor/github.com/Sirupsen/logrus/text_formatter.go new file mode 100644 index 0000000..17cc298 --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/text_formatter.go @@ -0,0 +1,159 @@ +package logrus + +import ( + "bytes" + "fmt" + "runtime" + "sort" + "strings" + "time" +) + +const ( + nocolor = 0 + red = 31 + green = 32 + yellow = 33 + blue = 34 + gray = 37 +) + +var ( + baseTimestamp time.Time + isTerminal bool +) + +func init() { + baseTimestamp = time.Now() + isTerminal = IsTerminal() +} + +func miniTS() int { + return int(time.Since(baseTimestamp) / time.Second) +} + +type TextFormatter struct { + // Set to true to bypass checking for a TTY before outputting colors. + ForceColors bool + + // Force disabling colors. + DisableColors bool + + // Disable timestamp logging. useful when output is redirected to logging + // system that already adds timestamps. + DisableTimestamp bool + + // Enable logging the full timestamp when a TTY is attached instead of just + // the time passed since beginning of execution. + FullTimestamp bool + + // TimestampFormat to use for display when a full timestamp is printed + TimestampFormat string + + // The fields are sorted by default for a consistent output. For applications + // that log extremely frequently and don't use the JSON formatter this may not + // be desired. + DisableSorting bool +} + +func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { + var keys []string = make([]string, 0, len(entry.Data)) + for k := range entry.Data { + keys = append(keys, k) + } + + if !f.DisableSorting { + sort.Strings(keys) + } + + b := &bytes.Buffer{} + + prefixFieldClashes(entry.Data) + + isColorTerminal := isTerminal && (runtime.GOOS != "windows") + isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors + + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = DefaultTimestampFormat + } + if isColored { + f.printColored(b, entry, keys, timestampFormat) + } else { + if !f.DisableTimestamp { + f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) + } + f.appendKeyValue(b, "level", entry.Level.String()) + f.appendKeyValue(b, "msg", entry.Message) + for _, key := range keys { + f.appendKeyValue(b, key, entry.Data[key]) + } + } + + b.WriteByte('\n') + return b.Bytes(), nil +} + +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { + var levelColor int + switch entry.Level { + case DebugLevel: + levelColor = gray + case WarnLevel: + levelColor = yellow + case ErrorLevel, FatalLevel, PanicLevel: + levelColor = red + default: + levelColor = blue + } + + levelText := strings.ToUpper(entry.Level.String())[0:4] + + if !f.FullTimestamp { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) + } else { + fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) + } + for _, k := range keys { + v := entry.Data[k] + fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) + } +} + +func needsQuoting(text string) bool { + for _, ch := range text { + if !((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '-' || ch == '.') { + return false + } + } + return true +} + +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { + + b.WriteString(key) + b.WriteByte('=') + + switch value := value.(type) { + case string: + if needsQuoting(value) { + b.WriteString(value) + } else { + fmt.Fprintf(b, "%q", value) + } + case error: + errmsg := value.Error() + if needsQuoting(errmsg) { + b.WriteString(errmsg) + } else { + fmt.Fprintf(b, "%q", value) + } + default: + fmt.Fprint(b, value) + } + + b.WriteByte(' ') +} diff --git a/vendor/github.com/Sirupsen/logrus/text_formatter_test.go b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go new file mode 100644 index 0000000..e25a44f --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/text_formatter_test.go @@ -0,0 +1,61 @@ +package logrus + +import ( + "bytes" + "errors" + "testing" + "time" +) + +func TestQuoting(t *testing.T) { + tf := &TextFormatter{DisableColors: true} + + checkQuoting := func(q bool, value interface{}) { + b, _ := tf.Format(WithField("test", value)) + idx := bytes.Index(b, ([]byte)("test=")) + cont := bytes.Contains(b[idx+5:], []byte{'"'}) + if cont != q { + if q { + t.Errorf("quoting expected for: %#v", value) + } else { + t.Errorf("quoting not expected for: %#v", value) + } + } + } + + checkQuoting(false, "abcd") + checkQuoting(false, "v1.0") + checkQuoting(false, "1234567890") + checkQuoting(true, "/foobar") + checkQuoting(true, "x y") + checkQuoting(true, "x,y") + checkQuoting(false, errors.New("invalid")) + checkQuoting(true, errors.New("invalid argument")) +} + +func TestTimestampFormat(t *testing.T) { + checkTimeStr := func(format string) { + customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} + customStr, _ := customFormatter.Format(WithField("test", "test")) + timeStart := bytes.Index(customStr, ([]byte)("time=")) + timeEnd := bytes.Index(customStr, ([]byte)("level=")) + timeStr := customStr[timeStart+5 : timeEnd-1] + if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { + timeStr = timeStr[1 : len(timeStr)-1] + } + if format == "" { + format = time.RFC3339 + } + _, e := time.Parse(format, (string)(timeStr)) + if e != nil { + t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) + } + } + + checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") + checkTimeStr("Mon Jan _2 15:04:05 2006") + checkTimeStr("") +} + +// TODO add tests for sorting etc., this requires a parser for the text +// formatter output. diff --git a/vendor/github.com/Sirupsen/logrus/writer.go b/vendor/github.com/Sirupsen/logrus/writer.go new file mode 100644 index 0000000..1e30b1c --- /dev/null +++ b/vendor/github.com/Sirupsen/logrus/writer.go @@ -0,0 +1,31 @@ +package logrus + +import ( + "bufio" + "io" + "runtime" +) + +func (logger *Logger) Writer() *io.PipeWriter { + reader, writer := io.Pipe() + + go logger.writerScanner(reader) + runtime.SetFinalizer(writer, writerFinalizer) + + return writer +} + +func (logger *Logger) writerScanner(reader *io.PipeReader) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + logger.Print(scanner.Text()) + } + if err := scanner.Err(); err != nil { + logger.Errorf("Error while reading from Writer: %s", err) + } + reader.Close() +} + +func writerFinalizer(writer *io.PipeWriter) { + writer.Close() +} diff --git a/vendor/github.com/codegangsta/cli/LICENSE b/vendor/github.com/codegangsta/cli/LICENSE new file mode 100644 index 0000000..5515ccf --- /dev/null +++ b/vendor/github.com/codegangsta/cli/LICENSE @@ -0,0 +1,21 @@ +Copyright (C) 2013 Jeremy Saenz +All Rights Reserved. + +MIT LICENSE + +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/codegangsta/cli/README.md b/vendor/github.com/codegangsta/cli/README.md new file mode 100644 index 0000000..85b9cda --- /dev/null +++ b/vendor/github.com/codegangsta/cli/README.md @@ -0,0 +1,308 @@ +[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) + +# cli.go +cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. + +You can view the API docs here: +http://godoc.org/github.com/codegangsta/cli + +## Overview +Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. + +**This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive! + +## Installation +Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html). + +To install `cli.go`, simply run: +``` +$ go get github.com/codegangsta/cli +``` + +Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: +``` +export PATH=$PATH:$GOPATH/bin +``` + +## Getting Started +One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`. + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + cli.NewApp().Run(os.Args) +} +``` + +This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "boom" + app.Usage = "make an explosive entrance" + app.Action = func(c *cli.Context) { + println("boom! I say!") + } + + app.Run(os.Args) +} +``` + +Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. + +## Example + +Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! + +Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "greet" + app.Usage = "fight the loneliness!" + app.Action = func(c *cli.Context) { + println("Hello friend!") + } + + app.Run(os.Args) +} +``` + +Install our command to the `$GOPATH/bin` directory: + +``` +$ go install +``` + +Finally run our new command: + +``` +$ greet +Hello friend! +``` + +cli.go also generates some bitchass help text: +``` +$ greet help +NAME: + greet - fight the loneliness! + +USAGE: + greet [global options] command [command options] [arguments...] + +VERSION: + 0.0.0 + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS + --version Shows version information +``` + +### Arguments +You can lookup arguments by calling the `Args` function on `cli.Context`. + +``` go +... +app.Action = func(c *cli.Context) { + println("Hello", c.Args()[0]) +} +... +``` + +### Flags +Setting and querying flags is simple. +``` go +... +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + }, +} +app.Action = func(c *cli.Context) { + name := "someone" + if len(c.Args()) > 0 { + name = c.Args()[0] + } + if c.String("lang") == "spanish" { + println("Hola", name) + } else { + println("Hello", name) + } +} +... +``` + +See full list of flags at http://godoc.org/github.com/codegangsta/cli + +#### Alternate Names + +You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "language for the greeting", + }, +} +``` + +That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. + +#### Values from the Environment + +You can also have the default value set from the environment via `EnvVar`. e.g. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "language for the greeting", + EnvVar: "APP_LANG", + }, +} +``` + +The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "language for the greeting", + EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", + }, +} +``` + +### Subcommands + +Subcommands can be defined for a more git-like command line app. +```go +... +app.Commands = []cli.Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(c *cli.Context) { + println("added task: ", c.Args().First()) + }, + }, + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + }, + { + Name: "template", + Aliases: []string{"r"}, + Usage: "options for task templates", + Subcommands: []cli.Command{ + { + Name: "add", + Usage: "add a new template", + Action: func(c *cli.Context) { + println("new task template: ", c.Args().First()) + }, + }, + { + Name: "remove", + Usage: "remove an existing template", + Action: func(c *cli.Context) { + println("removed task template: ", c.Args().First()) + }, + }, + }, + }, +} +... +``` + +### Bash Completion + +You can enable completion commands by setting the `EnableBashCompletion` +flag on the `App` object. By default, this setting will only auto-complete to +show an app's subcommands, but you can write your own completion methods for +the App or its subcommands. +```go +... +var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} +app := cli.NewApp() +app.EnableBashCompletion = true +app.Commands = []cli.Command{ + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + BashComplete: func(c *cli.Context) { + // This will complete if no args are passed + if len(c.Args()) > 0 { + return + } + for _, t := range tasks { + fmt.Println(t) + } + }, + } +} +... +``` + +#### To Enable + +Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while +setting the `PROG` variable to the name of your program: + +`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` + +#### To Distribute + +Copy and modify `autocomplete/bash_autocomplete` to use your program name +rather than `$PROG` and have the user copy the file into +`/etc/bash_completion.d/` (or automatically install it there if you are +distributing a package). Alternatively you can just document that users should +source the generic `autocomplete/bash_autocomplete` with `$PROG` set to your +program name in their bash configuration. + +## Contribution Guidelines +Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. + +If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together. + +If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out. diff --git a/vendor/github.com/codegangsta/cli/app.go b/vendor/github.com/codegangsta/cli/app.go new file mode 100644 index 0000000..e7caec9 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/app.go @@ -0,0 +1,308 @@ +package cli + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "time" +) + +// App is the main structure of a cli application. It is recomended that +// an app be created with the cli.NewApp() function +type App struct { + // The name of the program. Defaults to os.Args[0] + Name string + // Description of the program. + Usage string + // Version of the program + Version string + // List of commands to execute + Commands []Command + // List of flags to parse + Flags []Flag + // Boolean to enable bash completion commands + EnableBashCompletion bool + // Boolean to hide built-in help command + HideHelp bool + // Boolean to hide built-in version flag + HideVersion bool + // An action to execute when the bash-completion flag is set + BashComplete func(context *Context) + // An action to execute before any subcommands are run, but after the context is ready + // If a non-nil error is returned, no subcommands are run + Before func(context *Context) error + // An action to execute after any subcommands are run, but after the subcommand has finished + // It is run even if Action() panics + After func(context *Context) error + // The action to execute when no subcommands are specified + Action func(context *Context) + // Execute this function if the proper command cannot be found + CommandNotFound func(context *Context, command string) + // Compilation date + Compiled time.Time + // List of all authors who contributed + Authors []Author + // Copyright of the binary if any + Copyright string + // Name of Author (Note: Use App.Authors, this is deprecated) + Author string + // Email of Author (Note: Use App.Authors, this is deprecated) + Email string + // Writer writer to write output to + Writer io.Writer +} + +// Tries to find out when this binary was compiled. +// Returns the current time if it fails to find it. +func compileTime() time.Time { + info, err := os.Stat(os.Args[0]) + if err != nil { + return time.Now() + } + return info.ModTime() +} + +// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. +func NewApp() *App { + return &App{ + Name: os.Args[0], + Usage: "A new cli application", + Version: "0.0.0", + BashComplete: DefaultAppComplete, + Action: helpCommand.Action, + Compiled: compileTime(), + Writer: os.Stdout, + } +} + +// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination +func (a *App) Run(arguments []string) (err error) { + if a.Author != "" || a.Email != "" { + a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) + } + + // append help to commands + if a.Command(helpCommand.Name) == nil && !a.HideHelp { + a.Commands = append(a.Commands, helpCommand) + if (HelpFlag != BoolFlag{}) { + a.appendFlag(HelpFlag) + } + } + + //append version/help flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + + if !a.HideVersion { + a.appendFlag(VersionFlag) + } + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err = set.Parse(arguments[1:]) + nerr := normalizeFlags(a.Flags, set) + if nerr != nil { + fmt.Fprintln(a.Writer, nerr) + context := NewContext(a, set, nil) + ShowAppHelp(context) + return nerr + } + context := NewContext(a, set, nil) + + if err != nil { + fmt.Fprintln(a.Writer, "Incorrect Usage.") + fmt.Fprintln(a.Writer) + ShowAppHelp(context) + return err + } + + if checkCompletions(context) { + return nil + } + + if checkHelp(context) { + return nil + } + + if checkVersion(context) { + return nil + } + + if a.After != nil { + defer func() { + afterErr := a.After(context) + if afterErr != nil { + if err != nil { + err = NewMultiError(err, afterErr) + } else { + err = afterErr + } + } + }() + } + + if a.Before != nil { + err := a.Before(context) + if err != nil { + return err + } + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + a.Action(context) + return nil +} + +// Another entry point to the cli app, takes care of passing arguments and error handling +func (a *App) RunAndExitOnError() { + if err := a.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags +func (a *App) RunAsSubcommand(ctx *Context) (err error) { + // append help to commands + if len(a.Commands) > 0 { + if a.Command(helpCommand.Name) == nil && !a.HideHelp { + a.Commands = append(a.Commands, helpCommand) + if (HelpFlag != BoolFlag{}) { + a.appendFlag(HelpFlag) + } + } + } + + // append flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err = set.Parse(ctx.Args().Tail()) + nerr := normalizeFlags(a.Flags, set) + context := NewContext(a, set, ctx) + + if nerr != nil { + fmt.Fprintln(a.Writer, nerr) + fmt.Fprintln(a.Writer) + if len(a.Commands) > 0 { + ShowSubcommandHelp(context) + } else { + ShowCommandHelp(ctx, context.Args().First()) + } + return nerr + } + + if err != nil { + fmt.Fprintln(a.Writer, "Incorrect Usage.") + fmt.Fprintln(a.Writer) + ShowSubcommandHelp(context) + return err + } + + if checkCompletions(context) { + return nil + } + + if len(a.Commands) > 0 { + if checkSubcommandHelp(context) { + return nil + } + } else { + if checkCommandHelp(ctx, context.Args().First()) { + return nil + } + } + + if a.After != nil { + defer func() { + afterErr := a.After(context) + if afterErr != nil { + if err != nil { + err = NewMultiError(err, afterErr) + } else { + err = afterErr + } + } + }() + } + + if a.Before != nil { + err := a.Before(context) + if err != nil { + return err + } + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + a.Action(context) + + return nil +} + +// Returns the named command on App. Returns nil if the command does not exist +func (a *App) Command(name string) *Command { + for _, c := range a.Commands { + if c.HasName(name) { + return &c + } + } + + return nil +} + +func (a *App) hasFlag(flag Flag) bool { + for _, f := range a.Flags { + if flag == f { + return true + } + } + + return false +} + +func (a *App) appendFlag(flag Flag) { + if !a.hasFlag(flag) { + a.Flags = append(a.Flags, flag) + } +} + +// Author represents someone who has contributed to a cli project. +type Author struct { + Name string // The Authors name + Email string // The Authors email +} + +// String makes Author comply to the Stringer interface, to allow an easy print in the templating process +func (a Author) String() string { + e := "" + if a.Email != "" { + e = "<" + a.Email + "> " + } + + return fmt.Sprintf("%v %v", a.Name, e) +} diff --git a/vendor/github.com/codegangsta/cli/app_test.go b/vendor/github.com/codegangsta/cli/app_test.go new file mode 100644 index 0000000..4c6787a --- /dev/null +++ b/vendor/github.com/codegangsta/cli/app_test.go @@ -0,0 +1,867 @@ +package cli + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "strings" + "testing" +) + +func ExampleApp() { + // set args for examples sake + os.Args = []string{"greet", "--name", "Jeremy"} + + app := NewApp() + app.Name = "greet" + app.Flags = []Flag{ + StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Action = func(c *Context) { + fmt.Printf("Hello %v\n", c.String("name")) + } + app.Author = "Harrison" + app.Email = "harrison@lolwut.com" + app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} + app.Run(os.Args) + // Output: + // Hello Jeremy +} + +func ExampleAppSubcommand() { + // set args for examples sake + os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} + app := NewApp() + app.Name = "say" + app.Commands = []Command{ + { + Name: "hello", + Aliases: []string{"hi"}, + Usage: "use it to see a description", + Description: "This is how we describe hello the function", + Subcommands: []Command{ + { + Name: "english", + Aliases: []string{"en"}, + Usage: "sends a greeting in english", + Description: "greets someone in english", + Flags: []Flag{ + StringFlag{ + Name: "name", + Value: "Bob", + Usage: "Name of the person to greet", + }, + }, + Action: func(c *Context) { + fmt.Println("Hello,", c.String("name")) + }, + }, + }, + }, + } + + app.Run(os.Args) + // Output: + // Hello, Jeremy +} + +func ExampleAppHelp() { + // set args for examples sake + os.Args = []string{"greet", "h", "describeit"} + + app := NewApp() + app.Name = "greet" + app.Flags = []Flag{ + StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Commands = []Command{ + { + Name: "describeit", + Aliases: []string{"d"}, + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *Context) { + fmt.Printf("i like to describe things") + }, + }, + } + app.Run(os.Args) + // Output: + // NAME: + // describeit - use it to see a description + // + // USAGE: + // command describeit [arguments...] + // + // DESCRIPTION: + // This is how we describe describeit the function +} + +func ExampleAppBashComplete() { + // set args for examples sake + os.Args = []string{"greet", "--generate-bash-completion"} + + app := NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Commands = []Command{ + { + Name: "describeit", + Aliases: []string{"d"}, + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *Context) { + fmt.Printf("i like to describe things") + }, + }, { + Name: "next", + Usage: "next example", + Description: "more stuff to see when generating bash completion", + Action: func(c *Context) { + fmt.Printf("the next example") + }, + }, + } + + app.Run(os.Args) + // Output: + // describeit + // d + // next + // help + // h +} + +func TestApp_Run(t *testing.T) { + s := "" + + app := NewApp() + app.Action = func(c *Context) { + s = s + c.Args().First() + } + + err := app.Run([]string{"command", "foo"}) + expect(t, err, nil) + err = app.Run([]string{"command", "bar"}) + expect(t, err, nil) + expect(t, s, "foobar") +} + +var commandAppTests = []struct { + name string + expected bool +}{ + {"foobar", true}, + {"batbaz", true}, + {"b", true}, + {"f", true}, + {"bat", false}, + {"nothing", false}, +} + +func TestApp_Command(t *testing.T) { + app := NewApp() + fooCommand := Command{Name: "foobar", Aliases: []string{"f"}} + batCommand := Command{Name: "batbaz", Aliases: []string{"b"}} + app.Commands = []Command{ + fooCommand, + batCommand, + } + + for _, test := range commandAppTests { + expect(t, app.Command(test.name) != nil, test.expected) + } +} + +func TestApp_CommandWithArgBeforeFlags(t *testing.T) { + var parsedOption, firstArg string + + app := NewApp() + command := Command{ + Name: "cmd", + Flags: []Flag{ + StringFlag{Name: "option", Value: "", Usage: "some option"}, + }, + Action: func(c *Context) { + parsedOption = c.String("option") + firstArg = c.Args().First() + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) + + expect(t, parsedOption, "my-option") + expect(t, firstArg, "my-arg") +} + +func TestApp_RunAsSubcommandParseFlags(t *testing.T) { + var context *Context + + a := NewApp() + a.Commands = []Command{ + { + Name: "foo", + Action: func(c *Context) { + context = c + }, + Flags: []Flag{ + StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + }, + }, + Before: func(_ *Context) error { return nil }, + }, + } + a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) + + expect(t, context.Args().Get(0), "abcd") + expect(t, context.String("lang"), "spanish") +} + +func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { + var parsedOption string + var args []string + + app := NewApp() + command := Command{ + Name: "cmd", + Flags: []Flag{ + StringFlag{Name: "option", Value: "", Usage: "some option"}, + }, + Action: func(c *Context) { + parsedOption = c.String("option") + args = c.Args() + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"}) + + expect(t, parsedOption, "my-option") + expect(t, args[0], "my-arg") + expect(t, args[1], "--") + expect(t, args[2], "--notARealFlag") +} + +func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) { + var args []string + + app := NewApp() + command := Command{ + Name: "cmd", + Action: func(c *Context) { + args = c.Args() + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"}) + + expect(t, args[0], "my-arg") + expect(t, args[1], "--") + expect(t, args[2], "notAFlagAtAll") +} + +func TestApp_Float64Flag(t *testing.T) { + var meters float64 + + app := NewApp() + app.Flags = []Flag{ + Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, + } + app.Action = func(c *Context) { + meters = c.Float64("height") + } + + app.Run([]string{"", "--height", "1.93"}) + expect(t, meters, 1.93) +} + +func TestApp_ParseSliceFlags(t *testing.T) { + var parsedOption, firstArg string + var parsedIntSlice []int + var parsedStringSlice []string + + app := NewApp() + command := Command{ + Name: "cmd", + Flags: []Flag{ + IntSliceFlag{Name: "p", Value: &IntSlice{}, Usage: "set one or more ip addr"}, + StringSliceFlag{Name: "ip", Value: &StringSlice{}, Usage: "set one or more ports to open"}, + }, + Action: func(c *Context) { + parsedIntSlice = c.IntSlice("p") + parsedStringSlice = c.StringSlice("ip") + parsedOption = c.String("option") + firstArg = c.Args().First() + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) + + IntsEquals := func(a, b []int) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + + StrsEquals := func(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + var expectedIntSlice = []int{22, 80} + var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} + + if !IntsEquals(parsedIntSlice, expectedIntSlice) { + t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) + } + + if !StrsEquals(parsedStringSlice, expectedStringSlice) { + t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) + } +} + +func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) { + var parsedIntSlice []int + var parsedStringSlice []string + + app := NewApp() + command := Command{ + Name: "cmd", + Flags: []Flag{ + IntSliceFlag{Name: "a", Usage: "set numbers"}, + StringSliceFlag{Name: "str", Usage: "set strings"}, + }, + Action: func(c *Context) { + parsedIntSlice = c.IntSlice("a") + parsedStringSlice = c.StringSlice("str") + }, + } + app.Commands = []Command{command} + + app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"}) + + var expectedIntSlice = []int{2} + var expectedStringSlice = []string{"A"} + + if parsedIntSlice[0] != expectedIntSlice[0] { + t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0]) + } + + if parsedStringSlice[0] != expectedStringSlice[0] { + t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0]) + } +} + +func TestApp_DefaultStdout(t *testing.T) { + app := NewApp() + + if app.Writer != os.Stdout { + t.Error("Default output writer not set.") + } +} + +type mockWriter struct { + written []byte +} + +func (fw *mockWriter) Write(p []byte) (n int, err error) { + if fw.written == nil { + fw.written = p + } else { + fw.written = append(fw.written, p...) + } + + return len(p), nil +} + +func (fw *mockWriter) GetWritten() (b []byte) { + return fw.written +} + +func TestApp_SetStdout(t *testing.T) { + w := &mockWriter{} + + app := NewApp() + app.Name = "test" + app.Writer = w + + err := app.Run([]string{"help"}) + + if err != nil { + t.Fatalf("Run error: %s", err) + } + + if len(w.written) == 0 { + t.Error("App did not write output to desired writer.") + } +} + +func TestApp_BeforeFunc(t *testing.T) { + beforeRun, subcommandRun := false, false + beforeError := fmt.Errorf("fail") + var err error + + app := NewApp() + + app.Before = func(c *Context) error { + beforeRun = true + s := c.String("opt") + if s == "fail" { + return beforeError + } + + return nil + } + + app.Commands = []Command{ + Command{ + Name: "sub", + Action: func(c *Context) { + subcommandRun = true + }, + }, + } + + app.Flags = []Flag{ + StringFlag{Name: "opt"}, + } + + // run with the Before() func succeeding + err = app.Run([]string{"command", "--opt", "succeed", "sub"}) + + if err != nil { + t.Fatalf("Run error: %s", err) + } + + if beforeRun == false { + t.Errorf("Before() not executed when expected") + } + + if subcommandRun == false { + t.Errorf("Subcommand not executed when expected") + } + + // reset + beforeRun, subcommandRun = false, false + + // run with the Before() func failing + err = app.Run([]string{"command", "--opt", "fail", "sub"}) + + // should be the same error produced by the Before func + if err != beforeError { + t.Errorf("Run error expected, but not received") + } + + if beforeRun == false { + t.Errorf("Before() not executed when expected") + } + + if subcommandRun == true { + t.Errorf("Subcommand executed when NOT expected") + } + +} + +func TestApp_AfterFunc(t *testing.T) { + afterRun, subcommandRun := false, false + afterError := fmt.Errorf("fail") + var err error + + app := NewApp() + + app.After = func(c *Context) error { + afterRun = true + s := c.String("opt") + if s == "fail" { + return afterError + } + + return nil + } + + app.Commands = []Command{ + Command{ + Name: "sub", + Action: func(c *Context) { + subcommandRun = true + }, + }, + } + + app.Flags = []Flag{ + StringFlag{Name: "opt"}, + } + + // run with the After() func succeeding + err = app.Run([]string{"command", "--opt", "succeed", "sub"}) + + if err != nil { + t.Fatalf("Run error: %s", err) + } + + if afterRun == false { + t.Errorf("After() not executed when expected") + } + + if subcommandRun == false { + t.Errorf("Subcommand not executed when expected") + } + + // reset + afterRun, subcommandRun = false, false + + // run with the Before() func failing + err = app.Run([]string{"command", "--opt", "fail", "sub"}) + + // should be the same error produced by the Before func + if err != afterError { + t.Errorf("Run error expected, but not received") + } + + if afterRun == false { + t.Errorf("After() not executed when expected") + } + + if subcommandRun == false { + t.Errorf("Subcommand not executed when expected") + } +} + +func TestAppNoHelpFlag(t *testing.T) { + oldFlag := HelpFlag + defer func() { + HelpFlag = oldFlag + }() + + HelpFlag = BoolFlag{} + + app := NewApp() + err := app.Run([]string{"test", "-h"}) + + if err != flag.ErrHelp { + t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err) + } +} + +func TestAppHelpPrinter(t *testing.T) { + oldPrinter := HelpPrinter + defer func() { + HelpPrinter = oldPrinter + }() + + var wasCalled = false + HelpPrinter = func(w io.Writer, template string, data interface{}) { + wasCalled = true + } + + app := NewApp() + app.Run([]string{"-h"}) + + if wasCalled == false { + t.Errorf("Help printer expected to be called, but was not") + } +} + +func TestAppVersionPrinter(t *testing.T) { + oldPrinter := VersionPrinter + defer func() { + VersionPrinter = oldPrinter + }() + + var wasCalled = false + VersionPrinter = func(c *Context) { + wasCalled = true + } + + app := NewApp() + ctx := NewContext(app, nil, nil) + ShowVersion(ctx) + + if wasCalled == false { + t.Errorf("Version printer expected to be called, but was not") + } +} + +func TestAppCommandNotFound(t *testing.T) { + beforeRun, subcommandRun := false, false + app := NewApp() + + app.CommandNotFound = func(c *Context, command string) { + beforeRun = true + } + + app.Commands = []Command{ + Command{ + Name: "bar", + Action: func(c *Context) { + subcommandRun = true + }, + }, + } + + app.Run([]string{"command", "foo"}) + + expect(t, beforeRun, true) + expect(t, subcommandRun, false) +} + +func TestGlobalFlag(t *testing.T) { + var globalFlag string + var globalFlagSet bool + app := NewApp() + app.Flags = []Flag{ + StringFlag{Name: "global, g", Usage: "global"}, + } + app.Action = func(c *Context) { + globalFlag = c.GlobalString("global") + globalFlagSet = c.GlobalIsSet("global") + } + app.Run([]string{"command", "-g", "foo"}) + expect(t, globalFlag, "foo") + expect(t, globalFlagSet, true) + +} + +func TestGlobalFlagsInSubcommands(t *testing.T) { + subcommandRun := false + parentFlag := false + app := NewApp() + + app.Flags = []Flag{ + BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, + } + + app.Commands = []Command{ + Command{ + Name: "foo", + Flags: []Flag{ + BoolFlag{Name: "parent, p", Usage: "Parent flag"}, + }, + Subcommands: []Command{ + { + Name: "bar", + Action: func(c *Context) { + if c.GlobalBool("debug") { + subcommandRun = true + } + if c.GlobalBool("parent") { + parentFlag = true + } + }, + }, + }, + }, + } + + app.Run([]string{"command", "-d", "foo", "-p", "bar"}) + + expect(t, subcommandRun, true) + expect(t, parentFlag, true) +} + +func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) { + var subcommandHelpTopics = [][]string{ + {"command", "foo", "--help"}, + {"command", "foo", "-h"}, + {"command", "foo", "help"}, + } + + for _, flagSet := range subcommandHelpTopics { + t.Logf("==> checking with flags %v", flagSet) + + app := NewApp() + buf := new(bytes.Buffer) + app.Writer = buf + + subCmdBar := Command{ + Name: "bar", + Usage: "does bar things", + } + subCmdBaz := Command{ + Name: "baz", + Usage: "does baz things", + } + cmd := Command{ + Name: "foo", + Description: "descriptive wall of text about how it does foo things", + Subcommands: []Command{subCmdBar, subCmdBaz}, + } + + app.Commands = []Command{cmd} + err := app.Run(flagSet) + + if err != nil { + t.Error(err) + } + + output := buf.String() + t.Logf("output: %q\n", buf.Bytes()) + + if strings.Contains(output, "No help topic for") { + t.Errorf("expect a help topic, got none: \n%q", output) + } + + for _, shouldContain := range []string{ + cmd.Name, cmd.Description, + subCmdBar.Name, subCmdBar.Usage, + subCmdBaz.Name, subCmdBaz.Usage, + } { + if !strings.Contains(output, shouldContain) { + t.Errorf("want help to contain %q, did not: \n%q", shouldContain, output) + } + } + } +} + +func TestApp_Run_SubcommandFullPath(t *testing.T) { + app := NewApp() + buf := new(bytes.Buffer) + app.Writer = buf + + subCmd := Command{ + Name: "bar", + Usage: "does bar things", + } + cmd := Command{ + Name: "foo", + Description: "foo commands", + Subcommands: []Command{subCmd}, + } + app.Commands = []Command{cmd} + + err := app.Run([]string{"command", "foo", "bar", "--help"}) + if err != nil { + t.Error(err) + } + + output := buf.String() + if !strings.Contains(output, "foo bar - does bar things") { + t.Errorf("expected full path to subcommand: %s", output) + } + if !strings.Contains(output, "command foo bar [arguments...]") { + t.Errorf("expected full path to subcommand: %s", output) + } +} + +func TestApp_Run_Help(t *testing.T) { + var helpArguments = [][]string{{"boom", "--help"}, {"boom", "-h"}, {"boom", "help"}} + + for _, args := range helpArguments { + buf := new(bytes.Buffer) + + t.Logf("==> checking with arguments %v", args) + + app := NewApp() + app.Name = "boom" + app.Usage = "make an explosive entrance" + app.Writer = buf + app.Action = func(c *Context) { + buf.WriteString("boom I say!") + } + + err := app.Run(args) + if err != nil { + t.Error(err) + } + + output := buf.String() + t.Logf("output: %q\n", buf.Bytes()) + + if !strings.Contains(output, "boom - make an explosive entrance") { + t.Errorf("want help to contain %q, did not: \n%q", "boom - make an explosive entrance", output) + } + } +} + +func TestApp_Run_Version(t *testing.T) { + var versionArguments = [][]string{{"boom", "--version"}, {"boom", "-v"}} + + for _, args := range versionArguments { + buf := new(bytes.Buffer) + + t.Logf("==> checking with arguments %v", args) + + app := NewApp() + app.Name = "boom" + app.Usage = "make an explosive entrance" + app.Version = "0.1.0" + app.Writer = buf + app.Action = func(c *Context) { + buf.WriteString("boom I say!") + } + + err := app.Run(args) + if err != nil { + t.Error(err) + } + + output := buf.String() + t.Logf("output: %q\n", buf.Bytes()) + + if !strings.Contains(output, "0.1.0") { + t.Errorf("want version to contain %q, did not: \n%q", "0.1.0", output) + } + } +} + +func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { + app := NewApp() + app.Action = func(c *Context) {} + app.Before = func(c *Context) error { return fmt.Errorf("before error") } + app.After = func(c *Context) error { return fmt.Errorf("after error") } + + err := app.Run([]string{"foo"}) + if err == nil { + t.Fatalf("expected to recieve error from Run, got none") + } + + if !strings.Contains(err.Error(), "before error") { + t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) + } + if !strings.Contains(err.Error(), "after error") { + t.Errorf("expected text of error from After method, but got none in \"%v\"", err) + } +} + +func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { + app := NewApp() + app.Commands = []Command{ + Command{ + Name: "bar", + Before: func(c *Context) error { return fmt.Errorf("before error") }, + After: func(c *Context) error { return fmt.Errorf("after error") }, + }, + } + + err := app.Run([]string{"foo", "bar"}) + if err == nil { + t.Fatalf("expected to recieve error from Run, got none") + } + + if !strings.Contains(err.Error(), "before error") { + t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) + } + if !strings.Contains(err.Error(), "after error") { + t.Errorf("expected text of error from After method, but got none in \"%v\"", err) + } +} diff --git a/vendor/github.com/codegangsta/cli/cli.go b/vendor/github.com/codegangsta/cli/cli.go new file mode 100644 index 0000000..31dc912 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/cli.go @@ -0,0 +1,40 @@ +// Package cli provides a minimal framework for creating and organizing command line +// Go applications. cli is designed to be easy to understand and write, the most simple +// cli application can be written as follows: +// func main() { +// cli.NewApp().Run(os.Args) +// } +// +// Of course this application does not do much, so let's make this an actual application: +// func main() { +// app := cli.NewApp() +// app.Name = "greet" +// app.Usage = "say a greeting" +// app.Action = func(c *cli.Context) { +// println("Greetings") +// } +// +// app.Run(os.Args) +// } +package cli + +import ( + "strings" +) + +type MultiError struct { + Errors []error +} + +func NewMultiError(err ...error) MultiError { + return MultiError{Errors: err} +} + +func (m MultiError) Error() string { + errs := make([]string, len(m.Errors)) + for i, err := range m.Errors { + errs[i] = err.Error() + } + + return strings.Join(errs, "\n") +} diff --git a/vendor/github.com/codegangsta/cli/cli_test.go b/vendor/github.com/codegangsta/cli/cli_test.go new file mode 100644 index 0000000..e54f8e2 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/cli_test.go @@ -0,0 +1,98 @@ +package cli + +import ( + "os" +) + +func Example() { + app := NewApp() + app.Name = "todo" + app.Usage = "task list on the command line" + app.Commands = []Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(c *Context) { + println("added task: ", c.Args().First()) + }, + }, + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(c *Context) { + println("completed task: ", c.Args().First()) + }, + }, + } + + app.Run(os.Args) +} + +func ExampleSubcommand() { + app := NewApp() + app.Name = "say" + app.Commands = []Command{ + { + Name: "hello", + Aliases: []string{"hi"}, + Usage: "use it to see a description", + Description: "This is how we describe hello the function", + Subcommands: []Command{ + { + Name: "english", + Aliases: []string{"en"}, + Usage: "sends a greeting in english", + Description: "greets someone in english", + Flags: []Flag{ + StringFlag{ + Name: "name", + Value: "Bob", + Usage: "Name of the person to greet", + }, + }, + Action: func(c *Context) { + println("Hello, ", c.String("name")) + }, + }, { + Name: "spanish", + Aliases: []string{"sp"}, + Usage: "sends a greeting in spanish", + Flags: []Flag{ + StringFlag{ + Name: "surname", + Value: "Jones", + Usage: "Surname of the person to greet", + }, + }, + Action: func(c *Context) { + println("Hola, ", c.String("surname")) + }, + }, { + Name: "french", + Aliases: []string{"fr"}, + Usage: "sends a greeting in french", + Flags: []Flag{ + StringFlag{ + Name: "nickname", + Value: "Stevie", + Usage: "Nickname of the person to greet", + }, + }, + Action: func(c *Context) { + println("Bonjour, ", c.String("nickname")) + }, + }, + }, + }, { + Name: "bye", + Usage: "says goodbye", + Action: func(c *Context) { + println("bye") + }, + }, + } + + app.Run(os.Args) +} diff --git a/vendor/github.com/codegangsta/cli/command.go b/vendor/github.com/codegangsta/cli/command.go new file mode 100644 index 0000000..54617af --- /dev/null +++ b/vendor/github.com/codegangsta/cli/command.go @@ -0,0 +1,200 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "strings" +) + +// Command is a subcommand for a cli.App. +type Command struct { + // The name of the command + Name string + // short name of the command. Typically one character (deprecated, use `Aliases`) + ShortName string + // A list of aliases for the command + Aliases []string + // A short description of the usage of this command + Usage string + // A longer explanation of how the command works + Description string + // The function to call when checking for bash command completions + BashComplete func(context *Context) + // An action to execute before any sub-subcommands are run, but after the context is ready + // If a non-nil error is returned, no sub-subcommands are run + Before func(context *Context) error + // An action to execute after any subcommands are run, but after the subcommand has finished + // It is run even if Action() panics + After func(context *Context) error + // The function to call when this command is invoked + Action func(context *Context) + // List of child commands + Subcommands []Command + // List of flags to parse + Flags []Flag + // Treat all flags as normal arguments if true + SkipFlagParsing bool + // Boolean to hide built-in help command + HideHelp bool + + commandNamePath []string +} + +// Returns the full name of the command. +// For subcommands this ensures that parent commands are part of the command path +func (c Command) FullName() string { + if c.commandNamePath == nil { + return c.Name + } + return strings.Join(c.commandNamePath, " ") +} + +// Invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c Command) Run(ctx *Context) error { + if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil { + return c.startApp(ctx) + } + + if !c.HideHelp && (HelpFlag != BoolFlag{}) { + // append help to flags + c.Flags = append( + c.Flags, + HelpFlag, + ) + } + + if ctx.App.EnableBashCompletion { + c.Flags = append(c.Flags, BashCompletionFlag) + } + + set := flagSet(c.Name, c.Flags) + set.SetOutput(ioutil.Discard) + + firstFlagIndex := -1 + terminatorIndex := -1 + for index, arg := range ctx.Args() { + if arg == "--" { + terminatorIndex = index + break + } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { + firstFlagIndex = index + } + } + + var err error + if firstFlagIndex > -1 && !c.SkipFlagParsing { + args := ctx.Args() + regularArgs := make([]string, len(args[1:firstFlagIndex])) + copy(regularArgs, args[1:firstFlagIndex]) + + var flagArgs []string + if terminatorIndex > -1 { + flagArgs = args[firstFlagIndex:terminatorIndex] + regularArgs = append(regularArgs, args[terminatorIndex:]...) + } else { + flagArgs = args[firstFlagIndex:] + } + + err = set.Parse(append(flagArgs, regularArgs...)) + } else { + err = set.Parse(ctx.Args().Tail()) + } + + if err != nil { + fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") + fmt.Fprintln(ctx.App.Writer) + ShowCommandHelp(ctx, c.Name) + return err + } + + nerr := normalizeFlags(c.Flags, set) + if nerr != nil { + fmt.Fprintln(ctx.App.Writer, nerr) + fmt.Fprintln(ctx.App.Writer) + ShowCommandHelp(ctx, c.Name) + return nerr + } + context := NewContext(ctx.App, set, ctx) + + if checkCommandCompletions(context, c.Name) { + return nil + } + + if checkCommandHelp(context, c.Name) { + return nil + } + context.Command = c + c.Action(context) + return nil +} + +func (c Command) Names() []string { + names := []string{c.Name} + + if c.ShortName != "" { + names = append(names, c.ShortName) + } + + return append(names, c.Aliases...) +} + +// Returns true if Command.Name or Command.ShortName matches given name +func (c Command) HasName(name string) bool { + for _, n := range c.Names() { + if n == name { + return true + } + } + return false +} + +func (c Command) startApp(ctx *Context) error { + app := NewApp() + + // set the name and usage + app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) + if c.Description != "" { + app.Usage = c.Description + } else { + app.Usage = c.Usage + } + + // set CommandNotFound + app.CommandNotFound = ctx.App.CommandNotFound + + // set the flags and commands + app.Commands = c.Subcommands + app.Flags = c.Flags + app.HideHelp = c.HideHelp + + app.Version = ctx.App.Version + app.HideVersion = ctx.App.HideVersion + app.Compiled = ctx.App.Compiled + app.Author = ctx.App.Author + app.Email = ctx.App.Email + app.Writer = ctx.App.Writer + + // bash completion + app.EnableBashCompletion = ctx.App.EnableBashCompletion + if c.BashComplete != nil { + app.BashComplete = c.BashComplete + } + + // set the actions + app.Before = c.Before + app.After = c.After + if c.Action != nil { + app.Action = c.Action + } else { + app.Action = helpSubcommand.Action + } + + var newCmds []Command + for _, cc := range app.Commands { + cc.commandNamePath = []string{c.Name, cc.Name} + newCmds = append(newCmds, cc) + } + app.Commands = newCmds + + return app.RunAsSubcommand(ctx) +} diff --git a/vendor/github.com/codegangsta/cli/command_test.go b/vendor/github.com/codegangsta/cli/command_test.go new file mode 100644 index 0000000..688d12c --- /dev/null +++ b/vendor/github.com/codegangsta/cli/command_test.go @@ -0,0 +1,47 @@ +package cli + +import ( + "flag" + "testing" +) + +func TestCommandDoNotIgnoreFlags(t *testing.T) { + app := NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"blah", "blah", "-break"} + set.Parse(test) + + c := NewContext(app, set, nil) + + command := Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(_ *Context) {}, + } + err := command.Run(c) + + expect(t, err.Error(), "flag provided but not defined: -break") +} + +func TestCommandIgnoreFlags(t *testing.T) { + app := NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"blah", "blah"} + set.Parse(test) + + c := NewContext(app, set, nil) + + command := Command{ + Name: "test-cmd", + Aliases: []string{"tc"}, + Usage: "this is for testing", + Description: "testing", + Action: func(_ *Context) {}, + SkipFlagParsing: true, + } + err := command.Run(c) + + expect(t, err, nil) +} diff --git a/vendor/github.com/codegangsta/cli/context.go b/vendor/github.com/codegangsta/cli/context.go new file mode 100644 index 0000000..f541f41 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/context.go @@ -0,0 +1,388 @@ +package cli + +import ( + "errors" + "flag" + "strconv" + "strings" + "time" +) + +// Context is a type that is passed through to +// each Handler action in a cli application. Context +// can be used to retrieve context-specific Args and +// parsed command-line options. +type Context struct { + App *App + Command Command + flagSet *flag.FlagSet + setFlags map[string]bool + globalSetFlags map[string]bool + parentContext *Context +} + +// Creates a new context. For use in when invoking an App or Command action. +func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { + return &Context{App: app, flagSet: set, parentContext: parentCtx} +} + +// Looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Int(name string) int { + return lookupInt(name, c.flagSet) +} + +// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists +func (c *Context) Duration(name string) time.Duration { + return lookupDuration(name, c.flagSet) +} + +// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists +func (c *Context) Float64(name string) float64 { + return lookupFloat64(name, c.flagSet) +} + +// Looks up the value of a local bool flag, returns false if no bool flag exists +func (c *Context) Bool(name string) bool { + return lookupBool(name, c.flagSet) +} + +// Looks up the value of a local boolT flag, returns false if no bool flag exists +func (c *Context) BoolT(name string) bool { + return lookupBoolT(name, c.flagSet) +} + +// Looks up the value of a local string flag, returns "" if no string flag exists +func (c *Context) String(name string) string { + return lookupString(name, c.flagSet) +} + +// Looks up the value of a local string slice flag, returns nil if no string slice flag exists +func (c *Context) StringSlice(name string) []string { + return lookupStringSlice(name, c.flagSet) +} + +// Looks up the value of a local int slice flag, returns nil if no int slice flag exists +func (c *Context) IntSlice(name string) []int { + return lookupIntSlice(name, c.flagSet) +} + +// Looks up the value of a local generic flag, returns nil if no generic flag exists +func (c *Context) Generic(name string) interface{} { + return lookupGeneric(name, c.flagSet) +} + +// Looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalInt(name string) int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupInt(name, fs) + } + return 0 +} + +// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists +func (c *Context) GlobalDuration(name string) time.Duration { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupDuration(name, fs) + } + return 0 +} + +// Looks up the value of a global bool flag, returns false if no bool flag exists +func (c *Context) GlobalBool(name string) bool { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupBool(name, fs) + } + return false +} + +// Looks up the value of a global string flag, returns "" if no string flag exists +func (c *Context) GlobalString(name string) string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupString(name, fs) + } + return "" +} + +// Looks up the value of a global string slice flag, returns nil if no string slice flag exists +func (c *Context) GlobalStringSlice(name string) []string { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupStringSlice(name, fs) + } + return nil +} + +// Looks up the value of a global int slice flag, returns nil if no int slice flag exists +func (c *Context) GlobalIntSlice(name string) []int { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupIntSlice(name, fs) + } + return nil +} + +// Looks up the value of a global generic flag, returns nil if no generic flag exists +func (c *Context) GlobalGeneric(name string) interface{} { + if fs := lookupGlobalFlagSet(name, c); fs != nil { + return lookupGeneric(name, fs) + } + return nil +} + +// Returns the number of flags set +func (c *Context) NumFlags() int { + return c.flagSet.NFlag() +} + +// Determines if the flag was actually set +func (c *Context) IsSet(name string) bool { + if c.setFlags == nil { + c.setFlags = make(map[string]bool) + c.flagSet.Visit(func(f *flag.Flag) { + c.setFlags[f.Name] = true + }) + } + return c.setFlags[name] == true +} + +// Determines if the global flag was actually set +func (c *Context) GlobalIsSet(name string) bool { + if c.globalSetFlags == nil { + c.globalSetFlags = make(map[string]bool) + ctx := c + if ctx.parentContext != nil { + ctx = ctx.parentContext + } + for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext { + ctx.flagSet.Visit(func(f *flag.Flag) { + c.globalSetFlags[f.Name] = true + }) + } + } + return c.globalSetFlags[name] +} + +// Returns a slice of flag names used in this context. +func (c *Context) FlagNames() (names []string) { + for _, flag := range c.Command.Flags { + name := strings.Split(flag.getName(), ",")[0] + if name == "help" { + continue + } + names = append(names, name) + } + return +} + +// Returns a slice of global flag names used by the app. +func (c *Context) GlobalFlagNames() (names []string) { + for _, flag := range c.App.Flags { + name := strings.Split(flag.getName(), ",")[0] + if name == "help" || name == "version" { + continue + } + names = append(names, name) + } + return +} + +// Returns the parent context, if any +func (c *Context) Parent() *Context { + return c.parentContext +} + +type Args []string + +// Returns the command line arguments associated with the context. +func (c *Context) Args() Args { + args := Args(c.flagSet.Args()) + return args +} + +// Returns the nth argument, or else a blank string +func (a Args) Get(n int) string { + if len(a) > n { + return a[n] + } + return "" +} + +// Returns the first argument, or else a blank string +func (a Args) First() string { + return a.Get(0) +} + +// Return the rest of the arguments (not the first one) +// or else an empty string slice +func (a Args) Tail() []string { + if len(a) >= 2 { + return []string(a)[1:] + } + return []string{} +} + +// Checks if there are any arguments present +func (a Args) Present() bool { + return len(a) != 0 +} + +// Swaps arguments at the given indexes +func (a Args) Swap(from, to int) error { + if from >= len(a) || to >= len(a) { + return errors.New("index out of range") + } + a[from], a[to] = a[to], a[from] + return nil +} + +func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { + if ctx.parentContext != nil { + ctx = ctx.parentContext + } + for ; ctx != nil; ctx = ctx.parentContext { + if f := ctx.flagSet.Lookup(name); f != nil { + return ctx.flagSet + } + } + return nil +} + +func lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + val, err := strconv.Atoi(f.Value.String()) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupDuration(name string, set *flag.FlagSet) time.Duration { + f := set.Lookup(name) + if f != nil { + val, err := time.ParseDuration(f.Value.String()) + if err == nil { + return val + } + } + + return 0 +} + +func lookupFloat64(name string, set *flag.FlagSet) float64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseFloat(f.Value.String(), 64) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + return f.Value.String() + } + + return "" +} + +func lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*StringSlice)).Value() + + } + + return nil +} + +func lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*IntSlice)).Value() + + } + + return nil +} + +func lookupGeneric(name string, set *flag.FlagSet) interface{} { + f := set.Lookup(name) + if f != nil { + return f.Value + } + return nil +} + +func lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return val + } + + return false +} + +func lookupBoolT(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return true + } + return val + } + + return false +} + +func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { + switch ff.Value.(type) { + case *StringSlice: + default: + set.Set(name, ff.Value.String()) + } +} + +func normalizeFlags(flags []Flag, set *flag.FlagSet) error { + visited := make(map[string]bool) + set.Visit(func(f *flag.Flag) { + visited[f.Name] = true + }) + for _, f := range flags { + parts := strings.Split(f.getName(), ",") + if len(parts) == 1 { + continue + } + var ff *flag.Flag + for _, name := range parts { + name = strings.Trim(name, " ") + if visited[name] { + if ff != nil { + return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) + } + ff = set.Lookup(name) + } + } + if ff == nil { + continue + } + for _, name := range parts { + name = strings.Trim(name, " ") + if !visited[name] { + copyFlag(name, ff, set) + } + } + } + return nil +} diff --git a/vendor/github.com/codegangsta/cli/context_test.go b/vendor/github.com/codegangsta/cli/context_test.go new file mode 100644 index 0000000..7f8e928 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/context_test.go @@ -0,0 +1,113 @@ +package cli + +import ( + "flag" + "testing" + "time" +) + +func TestNewContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Int("myflag", 42, "doc") + globalCtx := NewContext(nil, globalSet, nil) + command := Command{Name: "mycommand"} + c := NewContext(nil, set, globalCtx) + c.Command = command + expect(t, c.Int("myflag"), 12) + expect(t, c.GlobalInt("myflag"), 42) + expect(t, c.Command.Name, "mycommand") +} + +func TestContext_Int(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + c := NewContext(nil, set, nil) + expect(t, c.Int("myflag"), 12) +} + +func TestContext_Duration(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Duration("myflag", time.Duration(12*time.Second), "doc") + c := NewContext(nil, set, nil) + expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) +} + +func TestContext_String(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.String("myflag", "hello world", "doc") + c := NewContext(nil, set, nil) + expect(t, c.String("myflag"), "hello world") +} + +func TestContext_Bool(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := NewContext(nil, set, nil) + expect(t, c.Bool("myflag"), false) +} + +func TestContext_BoolT(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", true, "doc") + c := NewContext(nil, set, nil) + expect(t, c.BoolT("myflag"), true) +} + +func TestContext_Args(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := NewContext(nil, set, nil) + set.Parse([]string{"--myflag", "bat", "baz"}) + expect(t, len(c.Args()), 2) + expect(t, c.Bool("myflag"), true) +} + +func TestContext_IsSet(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + set.String("otherflag", "hello world", "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Bool("myflagGlobal", true, "doc") + globalCtx := NewContext(nil, globalSet, nil) + c := NewContext(nil, set, globalCtx) + set.Parse([]string{"--myflag", "bat", "baz"}) + globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) + expect(t, c.IsSet("myflag"), true) + expect(t, c.IsSet("otherflag"), false) + expect(t, c.IsSet("bogusflag"), false) + expect(t, c.IsSet("myflagGlobal"), false) +} + +func TestContext_GlobalIsSet(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + set.String("otherflag", "hello world", "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Bool("myflagGlobal", true, "doc") + globalSet.Bool("myflagGlobalUnset", true, "doc") + globalCtx := NewContext(nil, globalSet, nil) + c := NewContext(nil, set, globalCtx) + set.Parse([]string{"--myflag", "bat", "baz"}) + globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) + expect(t, c.GlobalIsSet("myflag"), false) + expect(t, c.GlobalIsSet("otherflag"), false) + expect(t, c.GlobalIsSet("bogusflag"), false) + expect(t, c.GlobalIsSet("myflagGlobal"), true) + expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) + expect(t, c.GlobalIsSet("bogusGlobal"), false) +} + +func TestContext_NumFlags(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + set.String("otherflag", "hello world", "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Bool("myflagGlobal", true, "doc") + globalCtx := NewContext(nil, globalSet, nil) + c := NewContext(nil, set, globalCtx) + set.Parse([]string{"--myflag", "--otherflag=foo"}) + globalSet.Parse([]string{"--myflagGlobal"}) + expect(t, c.NumFlags(), 2) +} diff --git a/vendor/github.com/codegangsta/cli/flag.go b/vendor/github.com/codegangsta/cli/flag.go new file mode 100644 index 0000000..531b091 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/flag.go @@ -0,0 +1,497 @@ +package cli + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" +) + +// This flag enables bash-completion for all commands and subcommands +var BashCompletionFlag = BoolFlag{ + Name: "generate-bash-completion", +} + +// This flag prints the version for the application +var VersionFlag = BoolFlag{ + Name: "version, v", + Usage: "print the version", +} + +// This flag prints the help for all commands and subcommands +// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand +// unless HideHelp is set to true) +var HelpFlag = BoolFlag{ + Name: "help, h", + Usage: "show help", +} + +// Flag is a common interface related to parsing flags in cli. +// For more advanced flag parsing techniques, it is recomended that +// this interface be implemented. +type Flag interface { + fmt.Stringer + // Apply Flag settings to the given flag set + Apply(*flag.FlagSet) + getName() string +} + +func flagSet(name string, flags []Flag) *flag.FlagSet { + set := flag.NewFlagSet(name, flag.ContinueOnError) + + for _, f := range flags { + f.Apply(set) + } + return set +} + +func eachName(longName string, fn func(string)) { + parts := strings.Split(longName, ",") + for _, name := range parts { + name = strings.Trim(name, " ") + fn(name) + } +} + +// Generic is a generic parseable type identified by a specific flag +type Generic interface { + Set(value string) error + String() string +} + +// GenericFlag is the flag type for types implementing Generic +type GenericFlag struct { + Name string + Value Generic + Usage string + EnvVar string +} + +// String returns the string representation of the generic flag to display the +// help text to the user (uses the String() method of the generic flag to show +// the value) +func (f GenericFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s \"%v\"\t%v", prefixFor(f.Name), f.Name, f.Value, f.Usage)) +} + +// Apply takes the flagset and calls Set on the generic flag with the value +// provided by the user for parsing by the flag +func (f GenericFlag) Apply(set *flag.FlagSet) { + val := f.Value + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + val.Set(envVal) + break + } + } + } + + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f GenericFlag) getName() string { + return f.Name +} + +// StringSlice is an opaque type for []string to satisfy flag.Value +type StringSlice []string + +// Set appends the string value to the list of values +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) +} + +// Value returns the slice of strings set by this flag +func (f *StringSlice) Value() []string { + return *f +} + +// StringSlice is a string flag that can be specified multiple times on the +// command-line +type StringSliceFlag struct { + Name string + Value *StringSlice + Usage string + EnvVar string +} + +// String returns the usage +func (f StringSliceFlag) String() string { + firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") + pref := prefixFor(firstName) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f StringSliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + newVal := &StringSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + newVal.Set(s) + } + f.Value = newVal + break + } + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &StringSlice{} + } + set.Var(f.Value, name, f.Usage) + }) +} + +func (f StringSliceFlag) getName() string { + return f.Name +} + +// StringSlice is an opaque type for []int to satisfy flag.Value +type IntSlice []int + +// Set parses the value into an integer and appends it to the list of values +func (f *IntSlice) Set(value string) error { + tmp, err := strconv.Atoi(value) + if err != nil { + return err + } else { + *f = append(*f, tmp) + } + return nil +} + +// String returns a readable representation of this value (for usage defaults) +func (f *IntSlice) String() string { + return fmt.Sprintf("%d", *f) +} + +// Value returns the slice of ints set by this flag +func (f *IntSlice) Value() []int { + return *f +} + +// IntSliceFlag is an int flag that can be specified multiple times on the +// command-line +type IntSliceFlag struct { + Name string + Value *IntSlice + Usage string + EnvVar string +} + +// String returns the usage +func (f IntSliceFlag) String() string { + firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") + pref := prefixFor(firstName) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f IntSliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + newVal := &IntSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + err := newVal.Set(s) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + } + } + f.Value = newVal + break + } + } + } + + eachName(f.Name, func(name string) { + if f.Value == nil { + f.Value = &IntSlice{} + } + set.Var(f.Value, name, f.Usage) + }) +} + +func (f IntSliceFlag) getName() string { + return f.Name +} + +// BoolFlag is a switch that defaults to false +type BoolFlag struct { + Name string + Usage string + EnvVar string +} + +// String returns a readable representation of this value (for usage defaults) +func (f BoolFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f BoolFlag) Apply(set *flag.FlagSet) { + val := false + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValBool, err := strconv.ParseBool(envVal) + if err == nil { + val = envValBool + } + break + } + } + } + + eachName(f.Name, func(name string) { + set.Bool(name, val, f.Usage) + }) +} + +func (f BoolFlag) getName() string { + return f.Name +} + +// BoolTFlag this represents a boolean flag that is true by default, but can +// still be set to false by --some-flag=false +type BoolTFlag struct { + Name string + Usage string + EnvVar string +} + +// String returns a readable representation of this value (for usage defaults) +func (f BoolTFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f BoolTFlag) Apply(set *flag.FlagSet) { + val := true + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValBool, err := strconv.ParseBool(envVal) + if err == nil { + val = envValBool + break + } + } + } + } + + eachName(f.Name, func(name string) { + set.Bool(name, val, f.Usage) + }) +} + +func (f BoolTFlag) getName() string { + return f.Name +} + +// StringFlag represents a flag that takes as string value +type StringFlag struct { + Name string + Value string + Usage string + EnvVar string +} + +// String returns the usage +func (f StringFlag) String() string { + var fmtString string + fmtString = "%s %v\t%v" + + if len(f.Value) > 0 { + fmtString = "%s \"%v\"\t%v" + } else { + fmtString = "%s %v\t%v" + } + + return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f StringFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + f.Value = envVal + break + } + } + } + + eachName(f.Name, func(name string) { + set.String(name, f.Value, f.Usage) + }) +} + +func (f StringFlag) getName() string { + return f.Name +} + +// IntFlag is a flag that takes an integer +// Errors if the value provided cannot be parsed +type IntFlag struct { + Name string + Value int + Usage string + EnvVar string +} + +// String returns the usage +func (f IntFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f IntFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseInt(envVal, 0, 64) + if err == nil { + f.Value = int(envValInt) + break + } + } + } + } + + eachName(f.Name, func(name string) { + set.Int(name, f.Value, f.Usage) + }) +} + +func (f IntFlag) getName() string { + return f.Name +} + +// DurationFlag is a flag that takes a duration specified in Go's duration +// format: https://golang.org/pkg/time/#ParseDuration +type DurationFlag struct { + Name string + Value time.Duration + Usage string + EnvVar string +} + +// String returns a readable representation of this value (for usage defaults) +func (f DurationFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f DurationFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValDuration, err := time.ParseDuration(envVal) + if err == nil { + f.Value = envValDuration + break + } + } + } + } + + eachName(f.Name, func(name string) { + set.Duration(name, f.Value, f.Usage) + }) +} + +func (f DurationFlag) getName() string { + return f.Name +} + +// Float64Flag is a flag that takes an float value +// Errors if the value provided cannot be parsed +type Float64Flag struct { + Name string + Value float64 + Usage string + EnvVar string +} + +// String returns the usage +func (f Float64Flag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)) +} + +// Apply populates the flag given the flag set and environment +func (f Float64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValFloat, err := strconv.ParseFloat(envVal, 10) + if err == nil { + f.Value = float64(envValFloat) + } + } + } + } + + eachName(f.Name, func(name string) { + set.Float64(name, f.Value, f.Usage) + }) +} + +func (f Float64Flag) getName() string { + return f.Name +} + +func prefixFor(name string) (prefix string) { + if len(name) == 1 { + prefix = "-" + } else { + prefix = "--" + } + + return +} + +func prefixedNames(fullName string) (prefixed string) { + parts := strings.Split(fullName, ",") + for i, name := range parts { + name = strings.Trim(name, " ") + prefixed += prefixFor(name) + name + if i < len(parts)-1 { + prefixed += ", " + } + } + return +} + +func withEnvHint(envVar, str string) string { + envText := "" + if envVar != "" { + envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $")) + } + return str + envText +} diff --git a/vendor/github.com/codegangsta/cli/flag_test.go b/vendor/github.com/codegangsta/cli/flag_test.go new file mode 100644 index 0000000..3606102 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/flag_test.go @@ -0,0 +1,740 @@ +package cli + +import ( + "fmt" + "os" + "reflect" + "strings" + "testing" +) + +var boolFlagTests = []struct { + name string + expected string +}{ + {"help", "--help\t"}, + {"h", "-h\t"}, +} + +func TestBoolFlagHelpOutput(t *testing.T) { + + for _, test := range boolFlagTests { + flag := BoolFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +var stringFlagTests = []struct { + name string + value string + expected string +}{ + {"help", "", "--help \t"}, + {"h", "", "-h \t"}, + {"h", "", "-h \t"}, + {"test", "Something", "--test \"Something\"\t"}, +} + +func TestStringFlagHelpOutput(t *testing.T) { + + for _, test := range stringFlagTests { + flag := StringFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_FOO", "derp") + for _, test := range stringFlagTests { + flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_FOO]") { + t.Errorf("%s does not end with [$APP_FOO]", output) + } + } +} + +var stringSliceFlagTests = []struct { + name string + value *StringSlice + expected string +}{ + {"help", func() *StringSlice { + s := &StringSlice{} + s.Set("") + return s + }(), "--help [--help option --help option]\t"}, + {"h", func() *StringSlice { + s := &StringSlice{} + s.Set("") + return s + }(), "-h [-h option -h option]\t"}, + {"h", func() *StringSlice { + s := &StringSlice{} + s.Set("") + return s + }(), "-h [-h option -h option]\t"}, + {"test", func() *StringSlice { + s := &StringSlice{} + s.Set("Something") + return s + }(), "--test [--test option --test option]\t"}, +} + +func TestStringSliceFlagHelpOutput(t *testing.T) { + + for _, test := range stringSliceFlagTests { + flag := StringSliceFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_QWWX", "11,4") + for _, test := range stringSliceFlagTests { + flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_QWWX]") { + t.Errorf("%q does not end with [$APP_QWWX]", output) + } + } +} + +var intFlagTests = []struct { + name string + expected string +}{ + {"help", "--help \"0\"\t"}, + {"h", "-h \"0\"\t"}, +} + +func TestIntFlagHelpOutput(t *testing.T) { + + for _, test := range intFlagTests { + flag := IntFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2") + for _, test := range intFlagTests { + flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_BAR]") { + t.Errorf("%s does not end with [$APP_BAR]", output) + } + } +} + +var durationFlagTests = []struct { + name string + expected string +}{ + {"help", "--help \"0\"\t"}, + {"h", "-h \"0\"\t"}, +} + +func TestDurationFlagHelpOutput(t *testing.T) { + + for _, test := range durationFlagTests { + flag := DurationFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2h3m6s") + for _, test := range durationFlagTests { + flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_BAR]") { + t.Errorf("%s does not end with [$APP_BAR]", output) + } + } +} + +var intSliceFlagTests = []struct { + name string + value *IntSlice + expected string +}{ + {"help", &IntSlice{}, "--help [--help option --help option]\t"}, + {"h", &IntSlice{}, "-h [-h option -h option]\t"}, + {"h", &IntSlice{}, "-h [-h option -h option]\t"}, + {"test", func() *IntSlice { + i := &IntSlice{} + i.Set("9") + return i + }(), "--test [--test option --test option]\t"}, +} + +func TestIntSliceFlagHelpOutput(t *testing.T) { + + for _, test := range intSliceFlagTests { + flag := IntSliceFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_SMURF", "42,3") + for _, test := range intSliceFlagTests { + flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_SMURF]") { + t.Errorf("%q does not end with [$APP_SMURF]", output) + } + } +} + +var float64FlagTests = []struct { + name string + expected string +}{ + {"help", "--help \"0\"\t"}, + {"h", "-h \"0\"\t"}, +} + +func TestFloat64FlagHelpOutput(t *testing.T) { + + for _, test := range float64FlagTests { + flag := Float64Flag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAZ", "99.4") + for _, test := range float64FlagTests { + flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_BAZ]") { + t.Errorf("%s does not end with [$APP_BAZ]", output) + } + } +} + +var genericFlagTests = []struct { + name string + value Generic + expected string +}{ + {"test", &Parser{"abc", "def"}, "--test \"abc,def\"\ttest flag"}, + {"t", &Parser{"abc", "def"}, "-t \"abc,def\"\ttest flag"}, +} + +func TestGenericFlagHelpOutput(t *testing.T) { + + for _, test := range genericFlagTests { + flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_ZAP", "3") + for _, test := range genericFlagTests { + flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_ZAP]") { + t.Errorf("%s does not end with [$APP_ZAP]", output) + } + } +} + +func TestParseMultiString(t *testing.T) { + (&App{ + Flags: []Flag{ + StringFlag{Name: "serve, s"}, + }, + Action: func(ctx *Context) { + if ctx.String("serve") != "10" { + t.Errorf("main name not set") + } + if ctx.String("s") != "10" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10"}) +} + +func TestParseMultiStringFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_COUNT", "20") + (&App{ + Flags: []Flag{ + StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, + }, + Action: func(ctx *Context) { + if ctx.String("count") != "20" { + t.Errorf("main name not set") + } + if ctx.String("c") != "20" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiStringFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_COUNT", "20") + (&App{ + Flags: []Flag{ + StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, + }, + Action: func(ctx *Context) { + if ctx.String("count") != "20" { + t.Errorf("main name not set") + } + if ctx.String("c") != "20" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiStringSlice(t *testing.T) { + (&App{ + Flags: []Flag{ + StringSliceFlag{Name: "serve, s", Value: &StringSlice{}}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + +func TestParseMultiStringSliceFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&App{ + Flags: []Flag{ + StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&App{ + Flags: []Flag{ + StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiInt(t *testing.T) { + a := App{ + Flags: []Flag{ + IntFlag{Name: "serve, s"}, + }, + Action: func(ctx *Context) { + if ctx.Int("serve") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("s") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10"}) +} + +func TestParseMultiIntFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "10") + a := App{ + Flags: []Flag{ + IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *Context) { + if ctx.Int("timeout") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("t") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiIntFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "10") + a := App{ + Flags: []Flag{ + IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *Context) { + if ctx.Int("timeout") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("t") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiIntSlice(t *testing.T) { + (&App{ + Flags: []Flag{ + IntSliceFlag{Name: "serve, s", Value: &IntSlice{}}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + +func TestParseMultiIntSliceFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&App{ + Flags: []Flag{ + IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&App{ + Flags: []Flag{ + IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiFloat64(t *testing.T) { + a := App{ + Flags: []Flag{ + Float64Flag{Name: "serve, s"}, + }, + Action: func(ctx *Context) { + if ctx.Float64("serve") != 10.2 { + t.Errorf("main name not set") + } + if ctx.Float64("s") != 10.2 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10.2"}) +} + +func TestParseMultiFloat64FromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + a := App{ + Flags: []Flag{ + Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *Context) { + if ctx.Float64("timeout") != 15.5 { + t.Errorf("main name not set") + } + if ctx.Float64("t") != 15.5 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiFloat64FromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + a := App{ + Flags: []Flag{ + Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *Context) { + if ctx.Float64("timeout") != 15.5 { + t.Errorf("main name not set") + } + if ctx.Float64("t") != 15.5 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBool(t *testing.T) { + a := App{ + Flags: []Flag{ + BoolFlag{Name: "serve, s"}, + }, + Action: func(ctx *Context) { + if ctx.Bool("serve") != true { + t.Errorf("main name not set") + } + if ctx.Bool("s") != true { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "--serve"}) +} + +func TestParseMultiBoolFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "1") + a := App{ + Flags: []Flag{ + BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, + }, + Action: func(ctx *Context) { + if ctx.Bool("debug") != true { + t.Errorf("main name not set from env") + } + if ctx.Bool("d") != true { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBoolFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "1") + a := App{ + Flags: []Flag{ + BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, + }, + Action: func(ctx *Context) { + if ctx.Bool("debug") != true { + t.Errorf("main name not set from env") + } + if ctx.Bool("d") != true { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBoolT(t *testing.T) { + a := App{ + Flags: []Flag{ + BoolTFlag{Name: "serve, s"}, + }, + Action: func(ctx *Context) { + if ctx.BoolT("serve") != true { + t.Errorf("main name not set") + } + if ctx.BoolT("s") != true { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "--serve"}) +} + +func TestParseMultiBoolTFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "0") + a := App{ + Flags: []Flag{ + BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, + }, + Action: func(ctx *Context) { + if ctx.BoolT("debug") != false { + t.Errorf("main name not set from env") + } + if ctx.BoolT("d") != false { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBoolTFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "0") + a := App{ + Flags: []Flag{ + BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, + }, + Action: func(ctx *Context) { + if ctx.BoolT("debug") != false { + t.Errorf("main name not set from env") + } + if ctx.BoolT("d") != false { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +type Parser [2]string + +func (p *Parser) Set(value string) error { + parts := strings.Split(value, ",") + if len(parts) != 2 { + return fmt.Errorf("invalid format") + } + + (*p)[0] = parts[0] + (*p)[1] = parts[1] + + return nil +} + +func (p *Parser) String() string { + return fmt.Sprintf("%s,%s", p[0], p[1]) +} + +func TestParseGeneric(t *testing.T) { + a := App{ + Flags: []Flag{ + GenericFlag{Name: "serve, s", Value: &Parser{}}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10,20"}) +} + +func TestParseGenericFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_SERVE", "20,30") + a := App{ + Flags: []Flag{ + GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseGenericFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_FOO", "99,2000") + a := App{ + Flags: []Flag{ + GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, + }, + Action: func(ctx *Context) { + if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { + t.Errorf("value not set from env") + } + }, + } + a.Run([]string{"run"}) +} diff --git a/vendor/github.com/codegangsta/cli/help.go b/vendor/github.com/codegangsta/cli/help.go new file mode 100644 index 0000000..66ef2fb --- /dev/null +++ b/vendor/github.com/codegangsta/cli/help.go @@ -0,0 +1,238 @@ +package cli + +import ( + "fmt" + "io" + "strings" + "text/tabwriter" + "text/template" +) + +// The text template for the Default help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var AppHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} [arguments...] + {{if .Version}} +VERSION: + {{.Version}} + {{end}}{{if len .Authors}} +AUTHOR(S): + {{range .Authors}}{{ . }}{{end}} + {{end}}{{if .Commands}} +COMMANDS: + {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} + {{end}}{{end}}{{if .Flags}} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}}{{if .Copyright }} +COPYRIGHT: + {{.Copyright}} + {{end}} +` + +// The text template for the command help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var CommandHelpTemplate = `NAME: + {{.FullName}} - {{.Usage}} + +USAGE: + command {{.FullName}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + +DESCRIPTION: + {{.Description}}{{end}}{{if .Flags}} + +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{ end }} +` + +// The text template for the subcommand help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var SubcommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] + +COMMANDS: + {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} + {{end}}{{if .Flags}} +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` + +var helpCommand = Command{ + Name: "help", + Aliases: []string{"h"}, + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowAppHelp(c) + } + }, +} + +var helpSubcommand = Command{ + Name: "help", + Aliases: []string{"h"}, + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowSubcommandHelp(c) + } + }, +} + +// Prints help for the App or Command +type helpPrinter func(w io.Writer, templ string, data interface{}) + +var HelpPrinter helpPrinter = printHelp + +// Prints version for the App +var VersionPrinter = printVersion + +func ShowAppHelp(c *Context) { + HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) +} + +// Prints the list of subcommands as the default app completion method +func DefaultAppComplete(c *Context) { + for _, command := range c.App.Commands { + for _, name := range command.Names() { + fmt.Fprintln(c.App.Writer, name) + } + } +} + +// Prints help for the given command +func ShowCommandHelp(ctx *Context, command string) { + // show the subcommand help for a command with subcommands + if command == "" { + HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) + return + } + + for _, c := range ctx.App.Commands { + if c.HasName(command) { + HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) + return + } + } + + if ctx.App.CommandNotFound != nil { + ctx.App.CommandNotFound(ctx, command) + } else { + fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command) + } +} + +// Prints help for the given subcommand +func ShowSubcommandHelp(c *Context) { + ShowCommandHelp(c, c.Command.Name) +} + +// Prints the version number of the App +func ShowVersion(c *Context) { + VersionPrinter(c) +} + +func printVersion(c *Context) { + fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) +} + +// Prints the lists of commands within a given context +func ShowCompletions(c *Context) { + a := c.App + if a != nil && a.BashComplete != nil { + a.BashComplete(c) + } +} + +// Prints the custom completions for a given command +func ShowCommandCompletions(ctx *Context, command string) { + c := ctx.App.Command(command) + if c != nil && c.BashComplete != nil { + c.BashComplete(ctx) + } +} + +func printHelp(out io.Writer, templ string, data interface{}) { + funcMap := template.FuncMap{ + "join": strings.Join, + } + + w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0) + t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) + err := t.Execute(w, data) + if err != nil { + panic(err) + } + w.Flush() +} + +func checkVersion(c *Context) bool { + if c.GlobalBool("version") || c.GlobalBool("v") || c.Bool("version") || c.Bool("v") { + ShowVersion(c) + return true + } + + return false +} + +func checkHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") || c.Bool("h") || c.Bool("help") { + ShowAppHelp(c) + return true + } + + return false +} + +func checkCommandHelp(c *Context, name string) bool { + if c.Bool("h") || c.Bool("help") { + ShowCommandHelp(c, name) + return true + } + + return false +} + +func checkSubcommandHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") { + ShowSubcommandHelp(c) + return true + } + + return false +} + +func checkCompletions(c *Context) bool { + if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { + ShowCompletions(c) + return true + } + + return false +} + +func checkCommandCompletions(c *Context, name string) bool { + if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { + ShowCommandCompletions(c, name) + return true + } + + return false +} diff --git a/vendor/github.com/codegangsta/cli/help_test.go b/vendor/github.com/codegangsta/cli/help_test.go new file mode 100644 index 0000000..42d0284 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/help_test.go @@ -0,0 +1,36 @@ +package cli + +import ( + "bytes" + "testing" +) + +func Test_ShowAppHelp_NoAuthor(t *testing.T) { + output := new(bytes.Buffer) + app := NewApp() + app.Writer = output + + c := NewContext(app, nil, nil) + + ShowAppHelp(c) + + if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 { + t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):") + } +} + +func Test_ShowAppHelp_NoVersion(t *testing.T) { + output := new(bytes.Buffer) + app := NewApp() + app.Writer = output + + app.Version = "" + + c := NewContext(app, nil, nil) + + ShowAppHelp(c) + + if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 { + t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") + } +} diff --git a/vendor/github.com/codegangsta/cli/helpers_test.go b/vendor/github.com/codegangsta/cli/helpers_test.go new file mode 100644 index 0000000..3ce8e93 --- /dev/null +++ b/vendor/github.com/codegangsta/cli/helpers_test.go @@ -0,0 +1,19 @@ +package cli + +import ( + "reflect" + "testing" +) + +/* Test Helpers */ +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} diff --git a/vendor/github.com/jtolds/gls/LICENSE b/vendor/github.com/jtolds/gls/LICENSE new file mode 100644 index 0000000..9b4a822 --- /dev/null +++ b/vendor/github.com/jtolds/gls/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2013, Space Monkey, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/jtolds/gls/README.md b/vendor/github.com/jtolds/gls/README.md new file mode 100644 index 0000000..4ebb692 --- /dev/null +++ b/vendor/github.com/jtolds/gls/README.md @@ -0,0 +1,89 @@ +gls +=== + +Goroutine local storage + +### IMPORTANT NOTE ### + +It is my duty to point you to https://blog.golang.org/context, which is how +Google solves all of the problems you'd perhaps consider using this package +for at scale. + +One downside to Google's approach is that *all* of your functions must have +a new first argument, but after clearing that hurdle everything else is much +better. + +If you aren't interested in this warning, read on. + +### Huhwaht? Why? ### + +Every so often, a thread shows up on the +[golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some +form of goroutine-local-storage, or some kind of goroutine id, or some kind of +context. There are a few valid use cases for goroutine-local-storage, one of +the most prominent being log line context. One poster was interested in being +able to log an HTTP request context id in every log line in the same goroutine +as the incoming HTTP request, without having to change every library and +function call he was interested in logging. + +This would be pretty useful. Provided that you could get some kind of +goroutine-local-storage, you could call +[log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging +writer that checks goroutine-local-storage for some context information and +adds that context to your log lines. + +But alas, Andrew Gerrand's typically diplomatic answer to the question of +goroutine-local variables was: + +> We wouldn't even be having this discussion if thread local storage wasn't +> useful. But every feature comes at a cost, and in my opinion the cost of +> threadlocals far outweighs their benefits. They're just not a good fit for +> Go. + +So, yeah, that makes sense. That's a pretty good reason for why the language +won't support a specific and (relatively) unuseful feature that requires some +runtime changes, just for the sake of a little bit of log improvement. + +But does Go require runtime changes? + +### How it works ### + +Go has pretty fantastic introspective and reflective features, but one thing Go +doesn't give you is any kind of access to the stack pointer, or frame pointer, +or goroutine id, or anything contextual about your current stack. It gives you +access to your list of callers, but only along with program counters, which are +fixed at compile time. + +But it does give you the stack. + +So, we define 16 special functions and embed base-16 tags into the stack using +the call order of those 16 functions. Then, we can read our tags back out of +the stack looking at the callers list. + +We then use these tags as an index into a traditional map for implementing +this library. + +### What are people saying? ### + +"Wow, that's horrifying." + +"This is the most terrible thing I have seen in a very long time." + +"Where is it getting a context from? Is this serializing all the requests? +What the heck is the client being bound to? What are these tags? Why does he +need callers? Oh god no. No no no." + +### Docs ### + +Please see the docs at http://godoc.org/github.com/jtolds/gls + +### Related ### + +If you're okay relying on the string format of the current runtime stacktrace +including a unique goroutine id (not guaranteed by the spec or anything, but +very unlikely to change within a Go release), you might be able to squeeze +out a bit more performance by using this similar library, inspired by some +code Brad Fitzpatrick wrote for debugging his HTTP/2 library: +https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require +any knowledge of the string format of the runtime stacktrace, which +probably adds unnecessary overhead). diff --git a/vendor/github.com/jtolds/gls/context.go b/vendor/github.com/jtolds/gls/context.go new file mode 100644 index 0000000..94d7fbb --- /dev/null +++ b/vendor/github.com/jtolds/gls/context.go @@ -0,0 +1,150 @@ +// Package gls implements goroutine-local storage. +package gls + +import ( + "runtime" + "sync" +) + +const ( + maxCallers = 64 +) + +var ( + stackTagPool = &idPool{} + mgrRegistry = make(map[*ContextManager]bool) + mgrRegistryMtx sync.RWMutex +) + +// Values is simply a map of key types to value types. Used by SetValues to +// set multiple values at once. +type Values map[interface{}]interface{} + +func currentStack(skip int) []uintptr { + stack := make([]uintptr, maxCallers) + return stack[:runtime.Callers(2+skip, stack)] +} + +// ContextManager is the main entrypoint for interacting with +// Goroutine-local-storage. You can have multiple independent ContextManagers +// at any given time. ContextManagers are usually declared globally for a given +// class of context variables. You should use NewContextManager for +// construction. +type ContextManager struct { + mtx sync.RWMutex + values map[uint]Values +} + +// NewContextManager returns a brand new ContextManager. It also registers the +// new ContextManager in the ContextManager registry which is used by the Go +// method. ContextManagers are typically defined globally at package scope. +func NewContextManager() *ContextManager { + mgr := &ContextManager{values: make(map[uint]Values)} + mgrRegistryMtx.Lock() + defer mgrRegistryMtx.Unlock() + mgrRegistry[mgr] = true + return mgr +} + +// Unregister removes a ContextManager from the global registry, used by the +// Go method. Only intended for use when you're completely done with a +// ContextManager. Use of Unregister at all is rare. +func (m *ContextManager) Unregister() { + mgrRegistryMtx.Lock() + defer mgrRegistryMtx.Unlock() + delete(mgrRegistry, m) +} + +// SetValues takes a collection of values and a function to call for those +// values to be set in. Anything further down the stack will have the set +// values available through GetValue. SetValues will add new values or replace +// existing values of the same key and will not mutate or change values for +// previous stack frames. +// SetValues is slow (makes a copy of all current and new values for the new +// gls-context) in order to reduce the amount of lookups GetValue requires. +func (m *ContextManager) SetValues(new_values Values, context_call func()) { + if len(new_values) == 0 { + context_call() + return + } + + tags := readStackTags(currentStack(1)) + + m.mtx.Lock() + values := new_values + for _, tag := range tags { + if existing_values, ok := m.values[tag]; ok { + // oh, we found existing values, let's make a copy + values = make(Values, len(existing_values)+len(new_values)) + for key, val := range existing_values { + values[key] = val + } + for key, val := range new_values { + values[key] = val + } + break + } + } + new_tag := stackTagPool.Acquire() + m.values[new_tag] = values + m.mtx.Unlock() + defer func() { + m.mtx.Lock() + delete(m.values, new_tag) + m.mtx.Unlock() + stackTagPool.Release(new_tag) + }() + + addStackTag(new_tag, context_call) +} + +// GetValue will return a previously set value, provided that the value was set +// by SetValues somewhere higher up the stack. If the value is not found, ok +// will be false. +func (m *ContextManager) GetValue(key interface{}) (value interface{}, ok bool) { + + tags := readStackTags(currentStack(1)) + m.mtx.RLock() + defer m.mtx.RUnlock() + for _, tag := range tags { + if values, ok := m.values[tag]; ok { + value, ok := values[key] + return value, ok + } + } + return "", false +} + +func (m *ContextManager) getValues() Values { + tags := readStackTags(currentStack(2)) + m.mtx.RLock() + defer m.mtx.RUnlock() + for _, tag := range tags { + if values, ok := m.values[tag]; ok { + return values + } + } + return nil +} + +// Go preserves ContextManager values and Goroutine-local-storage across new +// goroutine invocations. The Go method makes a copy of all existing values on +// all registered context managers and makes sure they are still set after +// kicking off the provided function in a new goroutine. If you don't use this +// Go method instead of the standard 'go' keyword, you will lose values in +// ContextManagers, as goroutines have brand new stacks. +func Go(cb func()) { + mgrRegistryMtx.RLock() + defer mgrRegistryMtx.RUnlock() + + for mgr, _ := range mgrRegistry { + values := mgr.getValues() + if len(values) > 0 { + mgr_copy := mgr + cb_copy := cb + cb = func() { mgr_copy.SetValues(values, cb_copy) } + } + } + + go cb() +} diff --git a/vendor/github.com/jtolds/gls/context_test.go b/vendor/github.com/jtolds/gls/context_test.go new file mode 100644 index 0000000..ae5bde4 --- /dev/null +++ b/vendor/github.com/jtolds/gls/context_test.go @@ -0,0 +1,139 @@ +package gls + +import ( + "fmt" + "sync" + "testing" +) + +func TestContexts(t *testing.T) { + mgr1 := NewContextManager() + mgr2 := NewContextManager() + + CheckVal := func(mgr *ContextManager, key, exp_val string) { + val, ok := mgr.GetValue(key) + if len(exp_val) == 0 { + if ok { + t.Fatalf("expected no value for key %s, got %s", key, val) + } + return + } + if !ok { + t.Fatalf("expected value %s for key %s, got no value", + exp_val, key) + } + if exp_val != val { + t.Fatalf("expected value %s for key %s, got %s", exp_val, key, + val) + } + + } + + Check := func(exp_m1v1, exp_m1v2, exp_m2v1, exp_m2v2 string) { + CheckVal(mgr1, "key1", exp_m1v1) + CheckVal(mgr1, "key2", exp_m1v2) + CheckVal(mgr2, "key1", exp_m2v1) + CheckVal(mgr2, "key2", exp_m2v2) + } + + Check("", "", "", "") + mgr2.SetValues(Values{"key1": "val1c"}, func() { + Check("", "", "val1c", "") + mgr1.SetValues(Values{"key1": "val1a"}, func() { + Check("val1a", "", "val1c", "") + mgr1.SetValues(Values{"key2": "val1b"}, func() { + Check("val1a", "val1b", "val1c", "") + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + Check("", "", "", "") + }() + Go(func() { + defer wg.Done() + Check("val1a", "val1b", "val1c", "") + }) + wg.Wait() + }) + }) + }) +} + +func ExampleContextManager_SetValues() { + var ( + mgr = NewContextManager() + request_id_key = GenSym() + ) + + MyLog := func() { + if request_id, ok := mgr.GetValue(request_id_key); ok { + fmt.Println("My request id is:", request_id) + } else { + fmt.Println("No request id found") + } + } + + mgr.SetValues(Values{request_id_key: "12345"}, func() { + MyLog() + }) + MyLog() + + // Output: My request id is: 12345 + // No request id found +} + +func ExampleGo() { + var ( + mgr = NewContextManager() + request_id_key = GenSym() + ) + + MyLog := func() { + if request_id, ok := mgr.GetValue(request_id_key); ok { + fmt.Println("My request id is:", request_id) + } else { + fmt.Println("No request id found") + } + } + + mgr.SetValues(Values{request_id_key: "12345"}, func() { + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + MyLog() + }() + wg.Wait() + wg.Add(1) + Go(func() { + defer wg.Done() + MyLog() + }) + wg.Wait() + }) + + // Output: No request id found + // My request id is: 12345 +} + +func BenchmarkGetValue(b *testing.B) { + mgr := NewContextManager() + mgr.SetValues(Values{"test_key": "test_val"}, func() { + b.ResetTimer() + for i := 0; i < b.N; i++ { + val, ok := mgr.GetValue("test_key") + if !ok || val != "test_val" { + b.FailNow() + } + } + }) +} + +func BenchmarkSetValues(b *testing.B) { + mgr := NewContextManager() + for i := 0; i < b.N/2; i++ { + mgr.SetValues(Values{"test_key": "test_val"}, func() { + mgr.SetValues(Values{"test_key2": "test_val2"}, func() {}) + }) + } +} diff --git a/vendor/github.com/jtolds/gls/gen_sym.go b/vendor/github.com/jtolds/gls/gen_sym.go new file mode 100644 index 0000000..8d5fc24 --- /dev/null +++ b/vendor/github.com/jtolds/gls/gen_sym.go @@ -0,0 +1,13 @@ +package gls + +var ( + symPool = &idPool{} +) + +// ContextKey is a throwaway value you can use as a key to a ContextManager +type ContextKey struct{ id uint } + +// GenSym will return a brand new, never-before-used ContextKey +func GenSym() ContextKey { + return ContextKey{id: symPool.Acquire()} +} diff --git a/vendor/github.com/jtolds/gls/id_pool.go b/vendor/github.com/jtolds/gls/id_pool.go new file mode 100644 index 0000000..b7974ae --- /dev/null +++ b/vendor/github.com/jtolds/gls/id_pool.go @@ -0,0 +1,34 @@ +package gls + +// though this could probably be better at keeping ids smaller, the goal of +// this class is to keep a registry of the smallest unique integer ids +// per-process possible + +import ( + "sync" +) + +type idPool struct { + mtx sync.Mutex + released []uint + max_id uint +} + +func (p *idPool) Acquire() (id uint) { + p.mtx.Lock() + defer p.mtx.Unlock() + if len(p.released) > 0 { + id = p.released[len(p.released)-1] + p.released = p.released[:len(p.released)-1] + return id + } + id = p.max_id + p.max_id++ + return id +} + +func (p *idPool) Release(id uint) { + p.mtx.Lock() + defer p.mtx.Unlock() + p.released = append(p.released, id) +} diff --git a/vendor/github.com/jtolds/gls/stack_tags.go b/vendor/github.com/jtolds/gls/stack_tags.go new file mode 100644 index 0000000..562a2fa --- /dev/null +++ b/vendor/github.com/jtolds/gls/stack_tags.go @@ -0,0 +1,93 @@ +package gls + +// so, basically, we're going to encode integer tags in base-16 on the stack + +import ( + "reflect" + "runtime" +) + +const ( + bitWidth = 4 +) + +func addStackTag(tag uint, context_call func()) { + if context_call == nil { + return + } + markS(tag, context_call) +} + +func markS(tag uint, cb func()) { _m(tag, cb) } +func mark0(tag uint, cb func()) { _m(tag, cb) } +func mark1(tag uint, cb func()) { _m(tag, cb) } +func mark2(tag uint, cb func()) { _m(tag, cb) } +func mark3(tag uint, cb func()) { _m(tag, cb) } +func mark4(tag uint, cb func()) { _m(tag, cb) } +func mark5(tag uint, cb func()) { _m(tag, cb) } +func mark6(tag uint, cb func()) { _m(tag, cb) } +func mark7(tag uint, cb func()) { _m(tag, cb) } +func mark8(tag uint, cb func()) { _m(tag, cb) } +func mark9(tag uint, cb func()) { _m(tag, cb) } +func markA(tag uint, cb func()) { _m(tag, cb) } +func markB(tag uint, cb func()) { _m(tag, cb) } +func markC(tag uint, cb func()) { _m(tag, cb) } +func markD(tag uint, cb func()) { _m(tag, cb) } +func markE(tag uint, cb func()) { _m(tag, cb) } +func markF(tag uint, cb func()) { _m(tag, cb) } + +var pc_lookup = make(map[uintptr]int8, 17) +var mark_lookup [16]func(uint, func()) + +func init() { + setEntries := func(f func(uint, func()), v int8) { + pc_lookup[reflect.ValueOf(f).Pointer()] = v + if v >= 0 { + mark_lookup[v] = f + } + } + setEntries(markS, -0x1) + setEntries(mark0, 0x0) + setEntries(mark1, 0x1) + setEntries(mark2, 0x2) + setEntries(mark3, 0x3) + setEntries(mark4, 0x4) + setEntries(mark5, 0x5) + setEntries(mark6, 0x6) + setEntries(mark7, 0x7) + setEntries(mark8, 0x8) + setEntries(mark9, 0x9) + setEntries(markA, 0xa) + setEntries(markB, 0xb) + setEntries(markC, 0xc) + setEntries(markD, 0xd) + setEntries(markE, 0xe) + setEntries(markF, 0xf) +} + +func _m(tag_remainder uint, cb func()) { + if tag_remainder == 0 { + cb() + } else { + mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb) + } +} + +func readStackTags(stack []uintptr) (tags []uint) { + var current_tag uint + for _, pc := range stack { + pc = runtime.FuncForPC(pc).Entry() + val, ok := pc_lookup[pc] + if !ok { + continue + } + if val < 0 { + tags = append(tags, current_tag) + current_tag = 0 + continue + } + current_tag <<= bitWidth + current_tag += uint(val) + } + return +} diff --git a/vendor/github.com/kr/pty/License b/vendor/github.com/kr/pty/License new file mode 100644 index 0000000..6b7558b --- /dev/null +++ b/vendor/github.com/kr/pty/License @@ -0,0 +1,23 @@ +Copyright (c) 2011 Keith Rarick + +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/kr/pty/README.md b/vendor/github.com/kr/pty/README.md new file mode 100644 index 0000000..7b7900c --- /dev/null +++ b/vendor/github.com/kr/pty/README.md @@ -0,0 +1,36 @@ +# pty + +Pty is a Go package for using unix pseudo-terminals. + +## Install + + go get github.com/kr/pty + +## Example + +```go +package main + +import ( + "github.com/kr/pty" + "io" + "os" + "os/exec" +) + +func main() { + c := exec.Command("grep", "--color=auto", "bar") + f, err := pty.Start(c) + if err != nil { + panic(err) + } + + go func() { + f.Write([]byte("foo\n")) + f.Write([]byte("bar\n")) + f.Write([]byte("baz\n")) + f.Write([]byte{4}) // EOT + }() + io.Copy(os.Stdout, f) +} +``` diff --git a/vendor/github.com/kr/pty/doc.go b/vendor/github.com/kr/pty/doc.go new file mode 100644 index 0000000..190cfbe --- /dev/null +++ b/vendor/github.com/kr/pty/doc.go @@ -0,0 +1,16 @@ +// Package pty provides functions for working with Unix terminals. +package pty + +import ( + "errors" + "os" +) + +// ErrUnsupported is returned if a function is not +// available on the current platform. +var ErrUnsupported = errors.New("unsupported") + +// Opens a pty and its corresponding tty. +func Open() (pty, tty *os.File, err error) { + return open() +} diff --git a/vendor/github.com/kr/pty/ioctl.go b/vendor/github.com/kr/pty/ioctl.go new file mode 100644 index 0000000..5b856e8 --- /dev/null +++ b/vendor/github.com/kr/pty/ioctl.go @@ -0,0 +1,11 @@ +package pty + +import "syscall" + +func ioctl(fd, cmd, ptr uintptr) error { + _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr) + if e != 0 { + return e + } + return nil +} diff --git a/vendor/github.com/kr/pty/ioctl_bsd.go b/vendor/github.com/kr/pty/ioctl_bsd.go new file mode 100644 index 0000000..73b12c5 --- /dev/null +++ b/vendor/github.com/kr/pty/ioctl_bsd.go @@ -0,0 +1,39 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package pty + +// from +const ( + _IOC_VOID uintptr = 0x20000000 + _IOC_OUT uintptr = 0x40000000 + _IOC_IN uintptr = 0x80000000 + _IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN + _IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN + + _IOC_PARAM_SHIFT = 13 + _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1 +) + +func _IOC_PARM_LEN(ioctl uintptr) uintptr { + return (ioctl >> 16) & _IOC_PARAM_MASK +} + +func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num +} + +func _IO(group byte, ioctl_num uintptr) uintptr { + return _IOC(_IOC_VOID, group, ioctl_num, 0) +} + +func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_OUT, group, ioctl_num, param_len) +} + +func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_IN, group, ioctl_num, param_len) +} + +func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len) +} diff --git a/vendor/github.com/kr/pty/mktypes.bash b/vendor/github.com/kr/pty/mktypes.bash new file mode 100755 index 0000000..9952c88 --- /dev/null +++ b/vendor/github.com/kr/pty/mktypes.bash @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +GOOSARCH="${GOOS}_${GOARCH}" +case "$GOOSARCH" in +_* | *_ | _) + echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 + exit 1 + ;; +esac + +GODEFS="go tool cgo -godefs" + +$GODEFS types.go |gofmt > ztypes_$GOARCH.go + +case $GOOS in +freebsd) + $GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go + ;; +esac diff --git a/vendor/github.com/kr/pty/pty_darwin.go b/vendor/github.com/kr/pty/pty_darwin.go new file mode 100644 index 0000000..4f4d5ca --- /dev/null +++ b/vendor/github.com/kr/pty/pty_darwin.go @@ -0,0 +1,60 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + err = grantpt(p) + if err != nil { + return nil, nil, err + } + + err = unlockpt(p) + if err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) + + err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) + if err != nil { + return "", err + } + + for i, c := range n { + if c == 0 { + return string(n[:i]), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} + +func grantpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0) +} + +func unlockpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0) +} diff --git a/vendor/github.com/kr/pty/pty_freebsd.go b/vendor/github.com/kr/pty/pty_freebsd.go new file mode 100644 index 0000000..b341bab --- /dev/null +++ b/vendor/github.com/kr/pty/pty_freebsd.go @@ -0,0 +1,73 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func posix_openpt(oflag int) (fd int, err error) { + r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +func open() (pty, tty *os.File, err error) { + fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC) + if err != nil { + return nil, nil, err + } + + p := os.NewFile(uintptr(fd), "/dev/pts") + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctl_FIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + master, err := isptmaster(f.Fd()) + if err != nil { + return "", err + } + if !master { + return "", syscall.EINVAL + } + + const n = _C_SPECNAMELEN + 1 + var ( + buf = make([]byte, n) + arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))} + ) + err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg))) + if err != nil { + return "", err + } + + for i, c := range buf { + if c == 0 { + return string(buf[:i]), nil + } + } + return "", errors.New("FIODGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/kr/pty/pty_linux.go b/vendor/github.com/kr/pty/pty_linux.go new file mode 100644 index 0000000..cb901a2 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_linux.go @@ -0,0 +1,46 @@ +package pty + +import ( + "os" + "strconv" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + err = unlockpt(p) + if err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + var n _C_uint + err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) + if err != nil { + return "", err + } + return "/dev/pts/" + strconv.Itoa(int(n)), nil +} + +func unlockpt(f *os.File) error { + var u _C_int + // use TIOCSPTLCK with a zero valued arg to clear the slave pty lock + return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) +} diff --git a/vendor/github.com/kr/pty/pty_unsupported.go b/vendor/github.com/kr/pty/pty_unsupported.go new file mode 100644 index 0000000..898c730 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux,!darwin,!freebsd + +package pty + +import ( + "os" +) + +func open() (pty, tty *os.File, err error) { + return nil, nil, ErrUnsupported +} diff --git a/vendor/github.com/kr/pty/run.go b/vendor/github.com/kr/pty/run.go new file mode 100644 index 0000000..f0678d2 --- /dev/null +++ b/vendor/github.com/kr/pty/run.go @@ -0,0 +1,28 @@ +package pty + +import ( + "os" + "os/exec" + "syscall" +) + +// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +func Start(c *exec.Cmd) (pty *os.File, err error) { + pty, tty, err := Open() + if err != nil { + return nil, err + } + defer tty.Close() + c.Stdout = tty + c.Stdin = tty + c.Stderr = tty + c.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} + err = c.Start() + if err != nil { + pty.Close() + return nil, err + } + return pty, err +} diff --git a/vendor/github.com/kr/pty/types.go b/vendor/github.com/kr/pty/types.go new file mode 100644 index 0000000..5aecb6b --- /dev/null +++ b/vendor/github.com/kr/pty/types.go @@ -0,0 +1,10 @@ +// +build ignore + +package pty + +import "C" + +type ( + _C_int C.int + _C_uint C.uint +) diff --git a/vendor/github.com/kr/pty/types_freebsd.go b/vendor/github.com/kr/pty/types_freebsd.go new file mode 100644 index 0000000..ce3eb95 --- /dev/null +++ b/vendor/github.com/kr/pty/types_freebsd.go @@ -0,0 +1,15 @@ +// +build ignore + +package pty + +/* +#include +#include +*/ +import "C" + +const ( + _C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */ +) + +type fiodgnameArg C.struct_fiodgname_arg diff --git a/vendor/github.com/kr/pty/util.go b/vendor/github.com/kr/pty/util.go new file mode 100644 index 0000000..67c52d0 --- /dev/null +++ b/vendor/github.com/kr/pty/util.go @@ -0,0 +1,35 @@ +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +// Getsize returns the number of rows (lines) and cols (positions +// in each line) in terminal t. +func Getsize(t *os.File) (rows, cols int, err error) { + var ws winsize + err = windowrect(&ws, t.Fd()) + return int(ws.ws_row), int(ws.ws_col), err +} + +type winsize struct { + ws_row uint16 + ws_col uint16 + ws_xpixel uint16 + ws_ypixel uint16 +} + +func windowrect(ws *winsize, fd uintptr) error { + _, _, errno := syscall.Syscall( + syscall.SYS_IOCTL, + fd, + syscall.TIOCGWINSZ, + uintptr(unsafe.Pointer(ws)), + ) + if errno != 0 { + return syscall.Errno(errno) + } + return nil +} diff --git a/vendor/github.com/kr/pty/ztypes_386.go b/vendor/github.com/kr/pty/ztypes_386.go new file mode 100644 index 0000000..ff0b8fd --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_386.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_amd64.go b/vendor/github.com/kr/pty/ztypes_amd64.go new file mode 100644 index 0000000..ff0b8fd --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_amd64.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_arm.go b/vendor/github.com/kr/pty/ztypes_arm.go new file mode 100644 index 0000000..ff0b8fd --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_arm.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_arm64.go b/vendor/github.com/kr/pty/ztypes_arm64.go new file mode 100644 index 0000000..6c29a4b --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_arm64.go @@ -0,0 +1,11 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +// +build arm64 + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_386.go b/vendor/github.com/kr/pty/ztypes_freebsd_386.go new file mode 100644 index 0000000..d997537 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_386.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go b/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go new file mode 100644 index 0000000..5fa102f --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go @@ -0,0 +1,14 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Pad_cgo_0 [4]byte + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_arm.go b/vendor/github.com/kr/pty/ztypes_freebsd_arm.go new file mode 100644 index 0000000..d997537 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_arm.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_ppc64.go b/vendor/github.com/kr/pty/ztypes_ppc64.go new file mode 100644 index 0000000..4e1af84 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_ppc64.go @@ -0,0 +1,11 @@ +// +build ppc64 + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_ppc64le.go b/vendor/github.com/kr/pty/ztypes_ppc64le.go new file mode 100644 index 0000000..e6780f4 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_ppc64le.go @@ -0,0 +1,11 @@ +// +build ppc64le + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_s390x.go b/vendor/github.com/kr/pty/ztypes_s390x.go new file mode 100644 index 0000000..a7452b6 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_s390x.go @@ -0,0 +1,11 @@ +// +build s390x + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/smartystreets/assertions/LICENSE.md b/vendor/github.com/smartystreets/assertions/LICENSE.md new file mode 100644 index 0000000..48a3731 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/LICENSE.md @@ -0,0 +1,23 @@ +Copyright (c) 2015 SmartyStreets, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +NOTE: Various optional and subordinate components carry their own licensing +requirements and restrictions. Use of those components is subject to the terms +and conditions outlined the respective license of each component. diff --git a/vendor/github.com/smartystreets/assertions/README.md b/vendor/github.com/smartystreets/assertions/README.md new file mode 100644 index 0000000..fdbd632 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/README.md @@ -0,0 +1,528 @@ +# assertions +-- + import "github.com/smartystreets/assertions" + +Package assertions contains the implementations for all assertions which are +referenced in goconvey's `convey` package +(github.com/smartystreets/goconvey/convey) for use with the So(...) method. They +can also be used in traditional Go test functions and even in applicaitons. + +## Usage + +#### func GoConveyMode + +```go +func GoConveyMode(yes bool) +``` +GoConveyMode provides control over JSON serialization of failures. When using +the assertions in this package from the convey package JSON results are very +helpful and can be rendered in a DIFF view. In that case, this function will be +called with a true value to enable the JSON serialization. By default, the +assertions in this package will not serializer a JSON result, making standalone +ussage more convenient. + +#### func ShouldAlmostEqual + +```go +func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string +``` +ShouldAlmostEqual makes sure that two parameters are close enough to being +equal. The acceptable delta may be specified with a third argument, or a very +small default delta will be used. + +#### func ShouldBeBetween + +```go +func ShouldBeBetween(actual interface{}, expected ...interface{}) string +``` +ShouldBeBetween receives exactly three parameters: an actual value, a lower +bound, and an upper bound. It ensures that the actual value is between both +bounds (but not equal to either of them). + +#### func ShouldBeBetweenOrEqual + +```go +func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string +``` +ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a +lower bound, and an upper bound. It ensures that the actual value is between +both bounds or equal to one of them. + +#### func ShouldBeBlank + +```go +func ShouldBeBlank(actual interface{}, expected ...interface{}) string +``` +ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal +to "". + +#### func ShouldBeChronological + +```go +func ShouldBeChronological(actual interface{}, expected ...interface{}) string +``` +ShouldBeChronological receives a []time.Time slice and asserts that the are in +chronological order starting with the first time.Time as the earliest. + +#### func ShouldBeEmpty + +```go +func ShouldBeEmpty(actual interface{}, expected ...interface{}) string +``` +ShouldBeEmpty receives a single parameter (actual) and determines whether or not +calling len(actual) would return `0`. It obeys the rules specified by the len +function for determining length: http://golang.org/pkg/builtin/#len + +#### func ShouldBeFalse + +```go +func ShouldBeFalse(actual interface{}, expected ...interface{}) string +``` +ShouldBeFalse receives a single parameter and ensures that it is false. + +#### func ShouldBeGreaterThan + +```go +func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string +``` +ShouldBeGreaterThan receives exactly two parameters and ensures that the first +is greater than the second. + +#### func ShouldBeGreaterThanOrEqualTo + +```go +func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string +``` +ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that +the first is greater than or equal to the second. + +#### func ShouldBeIn + +```go +func ShouldBeIn(actual interface{}, expected ...interface{}) string +``` +ShouldBeIn receives at least 2 parameters. The first is a proposed member of the +collection that is passed in either as the second parameter, or of the +collection that is comprised of all the remaining parameters. This assertion +ensures that the proposed member is in the collection (using ShouldEqual). + +#### func ShouldBeLessThan + +```go +func ShouldBeLessThan(actual interface{}, expected ...interface{}) string +``` +ShouldBeLessThan receives exactly two parameters and ensures that the first is +less than the second. + +#### func ShouldBeLessThanOrEqualTo + +```go +func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string +``` +ShouldBeLessThan receives exactly two parameters and ensures that the first is +less than or equal to the second. + +#### func ShouldBeNil + +```go +func ShouldBeNil(actual interface{}, expected ...interface{}) string +``` +ShouldBeNil receives a single parameter and ensures that it is nil. + +#### func ShouldBeTrue + +```go +func ShouldBeTrue(actual interface{}, expected ...interface{}) string +``` +ShouldBeTrue receives a single parameter and ensures that it is true. + +#### func ShouldBeZeroValue + +```go +func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string +``` +ShouldBeZeroValue receives a single parameter and ensures that it is the Go +equivalent of the default value, or "zero" value. + +#### func ShouldContain + +```go +func ShouldContain(actual interface{}, expected ...interface{}) string +``` +ShouldContain receives exactly two parameters. The first is a slice and the +second is a proposed member. Membership is determined using ShouldEqual. + +#### func ShouldContainKey + +```go +func ShouldContainKey(actual interface{}, expected ...interface{}) string +``` +ShouldContainKey receives exactly two parameters. The first is a map and the +second is a proposed key. Keys are compared with a simple '=='. + +#### func ShouldContainSubstring + +```go +func ShouldContainSubstring(actual interface{}, expected ...interface{}) string +``` +ShouldContainSubstring receives exactly 2 string parameters and ensures that the +first contains the second as a substring. + +#### func ShouldEndWith + +```go +func ShouldEndWith(actual interface{}, expected ...interface{}) string +``` +ShouldEndWith receives exactly 2 string parameters and ensures that the first +ends with the second. + +#### func ShouldEqual + +```go +func ShouldEqual(actual interface{}, expected ...interface{}) string +``` +ShouldEqual receives exactly two parameters and does an equality check. + +#### func ShouldHappenAfter + +```go +func ShouldHappenAfter(actual interface{}, expected ...interface{}) string +``` +ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the +first happens after the second. + +#### func ShouldHappenBefore + +```go +func ShouldHappenBefore(actual interface{}, expected ...interface{}) string +``` +ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the +first happens before the second. + +#### func ShouldHappenBetween + +```go +func ShouldHappenBetween(actual interface{}, expected ...interface{}) string +``` +ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the +first happens between (not on) the second and third. + +#### func ShouldHappenOnOrAfter + +```go +func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string +``` +ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that +the first happens on or after the second. + +#### func ShouldHappenOnOrBefore + +```go +func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string +``` +ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that +the first happens on or before the second. + +#### func ShouldHappenOnOrBetween + +```go +func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string +``` +ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that +the first happens between or on the second and third. + +#### func ShouldHappenWithin + +```go +func ShouldHappenWithin(actual interface{}, expected ...interface{}) string +``` +ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 +arguments) and asserts that the first time.Time happens within or on the +duration specified relative to the other time.Time. + +#### func ShouldHaveSameTypeAs + +```go +func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string +``` +ShouldHaveSameTypeAs receives exactly two parameters and compares their +underlying types for equality. + +#### func ShouldImplement + +```go +func ShouldImplement(actual interface{}, expectedList ...interface{}) string +``` +ShouldImplement receives exactly two parameters and ensures that the first +implements the interface type of the second. + +#### func ShouldNotAlmostEqual + +```go +func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string +``` +ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual + +#### func ShouldNotBeBetween + +```go +func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string +``` +ShouldNotBeBetween receives exactly three parameters: an actual value, a lower +bound, and an upper bound. It ensures that the actual value is NOT between both +bounds. + +#### func ShouldNotBeBetweenOrEqual + +```go +func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string +``` +ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a +lower bound, and an upper bound. It ensures that the actual value is nopt +between the bounds nor equal to either of them. + +#### func ShouldNotBeBlank + +```go +func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string +``` +ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is +equal to "". + +#### func ShouldNotBeEmpty + +```go +func ShouldNotBeEmpty(actual interface{}, expected ...interface{}) string +``` +ShouldNotBeEmpty receives a single parameter (actual) and determines whether or +not calling len(actual) would return a value greater than zero. It obeys the +rules specified by the `len` function for determining length: +http://golang.org/pkg/builtin/#len + +#### func ShouldNotBeIn + +```go +func ShouldNotBeIn(actual interface{}, expected ...interface{}) string +``` +ShouldNotBeIn receives at least 2 parameters. The first is a proposed member of +the collection that is passed in either as the second parameter, or of the +collection that is comprised of all the remaining parameters. This assertion +ensures that the proposed member is NOT in the collection (using ShouldEqual). + +#### func ShouldNotBeNil + +```go +func ShouldNotBeNil(actual interface{}, expected ...interface{}) string +``` +ShouldNotBeNil receives a single parameter and ensures that it is not nil. + +#### func ShouldNotContain + +```go +func ShouldNotContain(actual interface{}, expected ...interface{}) string +``` +ShouldNotContain receives exactly two parameters. The first is a slice and the +second is a proposed member. Membership is determinied using ShouldEqual. + +#### func ShouldNotContainKey + +```go +func ShouldNotContainKey(actual interface{}, expected ...interface{}) string +``` +ShouldNotContainKey receives exactly two parameters. The first is a map and the +second is a proposed absent key. Keys are compared with a simple '=='. + +#### func ShouldNotContainSubstring + +```go +func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string +``` +ShouldNotContainSubstring receives exactly 2 string parameters and ensures that +the first does NOT contain the second as a substring. + +#### func ShouldNotEndWith + +```go +func ShouldNotEndWith(actual interface{}, expected ...interface{}) string +``` +ShouldEndWith receives exactly 2 string parameters and ensures that the first +does not end with the second. + +#### func ShouldNotEqual + +```go +func ShouldNotEqual(actual interface{}, expected ...interface{}) string +``` +ShouldNotEqual receives exactly two parameters and does an inequality check. + +#### func ShouldNotHappenOnOrBetween + +```go +func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string +``` +ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts +that the first does NOT happen between or on the second or third. + +#### func ShouldNotHappenWithin + +```go +func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string +``` +ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 +arguments) and asserts that the first time.Time does NOT happen within or on the +duration specified relative to the other time.Time. + +#### func ShouldNotHaveSameTypeAs + +```go +func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string +``` +ShouldNotHaveSameTypeAs receives exactly two parameters and compares their +underlying types for inequality. + +#### func ShouldNotImplement + +```go +func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string +``` +ShouldNotImplement receives exactly two parameters and ensures that the first +does NOT implement the interface type of the second. + +#### func ShouldNotPanic + +```go +func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string) +``` +ShouldNotPanic receives a void, niladic function and expects to execute the +function without any panic. + +#### func ShouldNotPanicWith + +```go +func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string) +``` +ShouldNotPanicWith receives a void, niladic function and expects to recover a +panic whose content differs from the second argument. + +#### func ShouldNotPointTo + +```go +func ShouldNotPointTo(actual interface{}, expected ...interface{}) string +``` +ShouldNotPointTo receives exactly two parameters and checks to see that they +point to different addresess. + +#### func ShouldNotResemble + +```go +func ShouldNotResemble(actual interface{}, expected ...interface{}) string +``` +ShouldNotResemble receives exactly two parameters and does an inverse deep equal +check (see reflect.DeepEqual) + +#### func ShouldNotStartWith + +```go +func ShouldNotStartWith(actual interface{}, expected ...interface{}) string +``` +ShouldNotStartWith receives exactly 2 string parameters and ensures that the +first does not start with the second. + +#### func ShouldPanic + +```go +func ShouldPanic(actual interface{}, expected ...interface{}) (message string) +``` +ShouldPanic receives a void, niladic function and expects to recover a panic. + +#### func ShouldPanicWith + +```go +func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string) +``` +ShouldPanicWith receives a void, niladic function and expects to recover a panic +with the second argument as the content. + +#### func ShouldPointTo + +```go +func ShouldPointTo(actual interface{}, expected ...interface{}) string +``` +ShouldPointTo receives exactly two parameters and checks to see that they point +to the same address. + +#### func ShouldResemble + +```go +func ShouldResemble(actual interface{}, expected ...interface{}) string +``` +ShouldResemble receives exactly two parameters and does a deep equal check (see +reflect.DeepEqual) + +#### func ShouldStartWith + +```go +func ShouldStartWith(actual interface{}, expected ...interface{}) string +``` +ShouldStartWith receives exactly 2 string parameters and ensures that the first +starts with the second. + +#### func So + +```go +func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) +``` +So is a convenience function (as opposed to an inconvenience function?) for +running assertions on arbitrary arguments in any context, be it for testing or +even application logging. It allows you to perform assertion-like behavior (and +get nicely formatted messages detailing discrepancies) but without the program +blowing up or panicking. All that is required is to import this package and call +`So` with one of the assertions exported by this package as the second +parameter. The first return parameter is a boolean indicating if the assertion +was true. The second return parameter is the well-formatted message showing why +an assertion was incorrect, or blank if the assertion was correct. + +Example: + + if ok, message := So(x, ShouldBeGreaterThan, y); !ok { + log.Println(message) + } + +#### type Assertion + +```go +type Assertion struct { +} +``` + + +#### func New + +```go +func New(t testingT) *Assertion +``` +New swallows the *testing.T struct and prints failed assertions using t.Error. +Example: assertions.New(t).So(1, should.Equal, 1) + +#### func (*Assertion) Failed + +```go +func (this *Assertion) Failed() bool +``` +Failed reports whether any calls to So (on this Assertion instance) have failed. + +#### func (*Assertion) So + +```go +func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool +``` +So calls the standalone So function and additionally, calls t.Error in failure +scenarios. + +#### type Serializer + +```go +type Serializer interface { + // contains filtered or unexported methods +} +``` diff --git a/vendor/github.com/smartystreets/assertions/assertions.goconvey b/vendor/github.com/smartystreets/assertions/assertions.goconvey new file mode 100644 index 0000000..e76cf27 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/assertions.goconvey @@ -0,0 +1,3 @@ +#ignore +-timeout=1s +-coverpkg=github.com/smartystreets/assertions,github.com/smartystreets/assertions/internal/oglematchers \ No newline at end of file diff --git a/vendor/github.com/smartystreets/assertions/collections.go b/vendor/github.com/smartystreets/assertions/collections.go new file mode 100644 index 0000000..d279acc --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/collections.go @@ -0,0 +1,195 @@ +package assertions + +import ( + "fmt" + "reflect" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +// ShouldContain receives exactly two parameters. The first is a slice and the +// second is a proposed member. Membership is determined using ShouldEqual. +func ShouldContain(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + if matchError := oglematchers.Contains(expected[0]).Matches(actual); matchError != nil { + typeName := reflect.TypeOf(actual) + + if fmt.Sprintf("%v", matchError) == "which is not a slice or array" { + return fmt.Sprintf(shouldHaveBeenAValidCollection, typeName) + } + return fmt.Sprintf(shouldHaveContained, typeName, expected[0]) + } + return success +} + +// ShouldNotContain receives exactly two parameters. The first is a slice and the +// second is a proposed member. Membership is determinied using ShouldEqual. +func ShouldNotContain(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + typeName := reflect.TypeOf(actual) + + if matchError := oglematchers.Contains(expected[0]).Matches(actual); matchError != nil { + if fmt.Sprintf("%v", matchError) == "which is not a slice or array" { + return fmt.Sprintf(shouldHaveBeenAValidCollection, typeName) + } + return success + } + return fmt.Sprintf(shouldNotHaveContained, typeName, expected[0]) +} + +// ShouldContainKey receives exactly two parameters. The first is a map and the +// second is a proposed key. Keys are compared with a simple '=='. +func ShouldContainKey(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + keys, isMap := mapKeys(actual) + if !isMap { + return fmt.Sprintf(shouldHaveBeenAValidMap, reflect.TypeOf(actual)) + } + + if !keyFound(keys, expected[0]) { + return fmt.Sprintf(shouldHaveContainedKey, reflect.TypeOf(actual), expected) + } + + return "" +} + +// ShouldNotContainKey receives exactly two parameters. The first is a map and the +// second is a proposed absent key. Keys are compared with a simple '=='. +func ShouldNotContainKey(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + keys, isMap := mapKeys(actual) + if !isMap { + return fmt.Sprintf(shouldHaveBeenAValidMap, reflect.TypeOf(actual)) + } + + if keyFound(keys, expected[0]) { + return fmt.Sprintf(shouldNotHaveContainedKey, reflect.TypeOf(actual), expected) + } + + return "" +} + +func mapKeys(m interface{}) ([]reflect.Value, bool) { + value := reflect.ValueOf(m) + if value.Kind() != reflect.Map { + return nil, false + } + return value.MapKeys(), true +} +func keyFound(keys []reflect.Value, expectedKey interface{}) bool { + found := false + for _, key := range keys { + if key.Interface() == expectedKey { + found = true + } + } + return found +} + +// ShouldBeIn receives at least 2 parameters. The first is a proposed member of the collection +// that is passed in either as the second parameter, or of the collection that is comprised +// of all the remaining parameters. This assertion ensures that the proposed member is in +// the collection (using ShouldEqual). +func ShouldBeIn(actual interface{}, expected ...interface{}) string { + if fail := atLeast(1, expected); fail != success { + return fail + } + + if len(expected) == 1 { + return shouldBeIn(actual, expected[0]) + } + return shouldBeIn(actual, expected) +} +func shouldBeIn(actual interface{}, expected interface{}) string { + if matchError := oglematchers.Contains(actual).Matches(expected); matchError != nil { + return fmt.Sprintf(shouldHaveBeenIn, actual, reflect.TypeOf(expected)) + } + return success +} + +// ShouldNotBeIn receives at least 2 parameters. The first is a proposed member of the collection +// that is passed in either as the second parameter, or of the collection that is comprised +// of all the remaining parameters. This assertion ensures that the proposed member is NOT in +// the collection (using ShouldEqual). +func ShouldNotBeIn(actual interface{}, expected ...interface{}) string { + if fail := atLeast(1, expected); fail != success { + return fail + } + + if len(expected) == 1 { + return shouldNotBeIn(actual, expected[0]) + } + return shouldNotBeIn(actual, expected) +} +func shouldNotBeIn(actual interface{}, expected interface{}) string { + if matchError := oglematchers.Contains(actual).Matches(expected); matchError == nil { + return fmt.Sprintf(shouldNotHaveBeenIn, actual, reflect.TypeOf(expected)) + } + return success +} + +// ShouldBeEmpty receives a single parameter (actual) and determines whether or not +// calling len(actual) would return `0`. It obeys the rules specified by the len +// function for determining length: http://golang.org/pkg/builtin/#len +func ShouldBeEmpty(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } + + if actual == nil { + return success + } + + value := reflect.ValueOf(actual) + switch value.Kind() { + case reflect.Slice: + if value.Len() == 0 { + return success + } + case reflect.Chan: + if value.Len() == 0 { + return success + } + case reflect.Map: + if value.Len() == 0 { + return success + } + case reflect.String: + if value.Len() == 0 { + return success + } + case reflect.Ptr: + elem := value.Elem() + kind := elem.Kind() + if (kind == reflect.Slice || kind == reflect.Array) && elem.Len() == 0 { + return success + } + } + + return fmt.Sprintf(shouldHaveBeenEmpty, actual) +} + +// ShouldNotBeEmpty receives a single parameter (actual) and determines whether or not +// calling len(actual) would return a value greater than zero. It obeys the rules +// specified by the `len` function for determining length: http://golang.org/pkg/builtin/#len +func ShouldNotBeEmpty(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } + + if empty := ShouldBeEmpty(actual, expected...); empty != success { + return success + } + return fmt.Sprintf(shouldNotHaveBeenEmpty, actual) +} diff --git a/vendor/github.com/smartystreets/assertions/collections_test.go b/vendor/github.com/smartystreets/assertions/collections_test.go new file mode 100644 index 0000000..ba4afb4 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/collections_test.go @@ -0,0 +1,125 @@ +package assertions + +import ( + "fmt" + "testing" + "time" +) + +func TestShouldContainKey(t *testing.T) { + fail(t, so(map[int]int{}, ShouldContainKey), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(map[int]int{}, ShouldContainKey, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(Thing1{}, ShouldContainKey, 1), "You must provide a valid map type (was assertions.Thing1)!") + fail(t, so(nil, ShouldContainKey, 1), "You must provide a valid map type (was )!") + fail(t, so(map[int]int{1: 41}, ShouldContainKey, 2), "Expected the map[int]int to contain the key: [2] (but it didn't)!") + + pass(t, so(map[int]int{1: 41}, ShouldContainKey, 1)) + pass(t, so(map[int]int{1: 41, 2: 42, 3: 43}, ShouldContainKey, 2)) +} + +func TestShouldNotContainKey(t *testing.T) { + fail(t, so(map[int]int{}, ShouldNotContainKey), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(map[int]int{}, ShouldNotContainKey, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(Thing1{}, ShouldNotContainKey, 1), "You must provide a valid map type (was assertions.Thing1)!") + fail(t, so(nil, ShouldNotContainKey, 1), "You must provide a valid map type (was )!") + fail(t, so(map[int]int{1: 41}, ShouldNotContainKey, 1), "Expected the map[int]int NOT to contain the key: [1] (but it did)!") + pass(t, so(map[int]int{1: 41}, ShouldNotContainKey, 2)) +} + +func TestShouldContain(t *testing.T) { + fail(t, so([]int{}, ShouldContain), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so([]int{}, ShouldContain, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(Thing1{}, ShouldContain, 1), "You must provide a valid container (was assertions.Thing1)!") + fail(t, so(nil, ShouldContain, 1), "You must provide a valid container (was )!") + fail(t, so([]int{1}, ShouldContain, 2), "Expected the container ([]int) to contain: '2' (but it didn't)!") + + pass(t, so([]int{1}, ShouldContain, 1)) + pass(t, so([]int{1, 2, 3}, ShouldContain, 2)) +} + +func TestShouldNotContain(t *testing.T) { + fail(t, so([]int{}, ShouldNotContain), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so([]int{}, ShouldNotContain, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(Thing1{}, ShouldNotContain, 1), "You must provide a valid container (was assertions.Thing1)!") + fail(t, so(nil, ShouldNotContain, 1), "You must provide a valid container (was )!") + + fail(t, so([]int{1}, ShouldNotContain, 1), "Expected the container ([]int) NOT to contain: '1' (but it did)!") + fail(t, so([]int{1, 2, 3}, ShouldNotContain, 2), "Expected the container ([]int) NOT to contain: '2' (but it did)!") + + pass(t, so([]int{1}, ShouldNotContain, 2)) +} + +func TestShouldBeIn(t *testing.T) { + fail(t, so(4, ShouldBeIn), needNonEmptyCollection) + + container := []int{1, 2, 3, 4} + pass(t, so(4, ShouldBeIn, container)) + pass(t, so(4, ShouldBeIn, 1, 2, 3, 4)) + + fail(t, so(4, ShouldBeIn, 1, 2, 3), "Expected '4' to be in the container ([]interface {}), but it wasn't!") + fail(t, so(4, ShouldBeIn, []int{1, 2, 3}), "Expected '4' to be in the container ([]int), but it wasn't!") +} + +func TestShouldNotBeIn(t *testing.T) { + fail(t, so(4, ShouldNotBeIn), needNonEmptyCollection) + + container := []int{1, 2, 3, 4} + pass(t, so(42, ShouldNotBeIn, container)) + pass(t, so(42, ShouldNotBeIn, 1, 2, 3, 4)) + + fail(t, so(2, ShouldNotBeIn, 1, 2, 3), "Expected '2' NOT to be in the container ([]interface {}), but it was!") + fail(t, so(2, ShouldNotBeIn, []int{1, 2, 3}), "Expected '2' NOT to be in the container ([]int), but it was!") +} + +func TestShouldBeEmpty(t *testing.T) { + fail(t, so(1, ShouldBeEmpty, 2, 3), "This assertion requires exactly 0 comparison values (you provided 2).") + + pass(t, so([]int{}, ShouldBeEmpty)) // empty slice + pass(t, so([]interface{}{}, ShouldBeEmpty)) // empty slice + pass(t, so(map[string]int{}, ShouldBeEmpty)) // empty map + pass(t, so("", ShouldBeEmpty)) // empty string + pass(t, so(&[]int{}, ShouldBeEmpty)) // pointer to empty slice + pass(t, so(&[0]int{}, ShouldBeEmpty)) // pointer to empty array + pass(t, so(nil, ShouldBeEmpty)) // nil + pass(t, so(make(chan string), ShouldBeEmpty)) // empty channel + + fail(t, so([]int{1}, ShouldBeEmpty), "Expected [1] to be empty (but it wasn't)!") // non-empty slice + fail(t, so([]interface{}{1}, ShouldBeEmpty), "Expected [1] to be empty (but it wasn't)!") // non-empty slice + fail(t, so(map[string]int{"hi": 0}, ShouldBeEmpty), "Expected map[hi:0] to be empty (but it wasn't)!") // non-empty map + fail(t, so("hi", ShouldBeEmpty), "Expected hi to be empty (but it wasn't)!") // non-empty string + fail(t, so(&[]int{1}, ShouldBeEmpty), "Expected &[1] to be empty (but it wasn't)!") // pointer to non-empty slice + fail(t, so(&[1]int{1}, ShouldBeEmpty), "Expected &[1] to be empty (but it wasn't)!") // pointer to non-empty array + c := make(chan int, 1) // non-empty channel + go func() { c <- 1 }() + time.Sleep(time.Millisecond) + fail(t, so(c, ShouldBeEmpty), fmt.Sprintf("Expected %+v to be empty (but it wasn't)!", c)) +} + +func TestShouldNotBeEmpty(t *testing.T) { + fail(t, so(1, ShouldNotBeEmpty, 2, 3), "This assertion requires exactly 0 comparison values (you provided 2).") + + fail(t, so([]int{}, ShouldNotBeEmpty), "Expected [] to NOT be empty (but it was)!") // empty slice + fail(t, so([]interface{}{}, ShouldNotBeEmpty), "Expected [] to NOT be empty (but it was)!") // empty slice + fail(t, so(map[string]int{}, ShouldNotBeEmpty), "Expected map[] to NOT be empty (but it was)!") // empty map + fail(t, so("", ShouldNotBeEmpty), "Expected to NOT be empty (but it was)!") // empty string + fail(t, so(&[]int{}, ShouldNotBeEmpty), "Expected &[] to NOT be empty (but it was)!") // pointer to empty slice + fail(t, so(&[0]int{}, ShouldNotBeEmpty), "Expected &[] to NOT be empty (but it was)!") // pointer to empty array + fail(t, so(nil, ShouldNotBeEmpty), "Expected to NOT be empty (but it was)!") // nil + c := make(chan int, 0) // non-empty channel + fail(t, so(c, ShouldNotBeEmpty), fmt.Sprintf("Expected %+v to NOT be empty (but it was)!", c)) // empty channel + + pass(t, so([]int{1}, ShouldNotBeEmpty)) // non-empty slice + pass(t, so([]interface{}{1}, ShouldNotBeEmpty)) // non-empty slice + pass(t, so(map[string]int{"hi": 0}, ShouldNotBeEmpty)) // non-empty map + pass(t, so("hi", ShouldNotBeEmpty)) // non-empty string + pass(t, so(&[]int{1}, ShouldNotBeEmpty)) // pointer to non-empty slice + pass(t, so(&[1]int{1}, ShouldNotBeEmpty)) // pointer to non-empty array + c = make(chan int, 1) + go func() { c <- 1 }() + time.Sleep(time.Millisecond) + pass(t, so(c, ShouldNotBeEmpty)) +} diff --git a/vendor/github.com/smartystreets/assertions/doc.go b/vendor/github.com/smartystreets/assertions/doc.go new file mode 100644 index 0000000..d3d116c --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/doc.go @@ -0,0 +1,99 @@ +// Package assertions contains the implementations for all assertions which +// are referenced in goconvey's `convey` package +// (github.com/smartystreets/goconvey/convey) for use with the So(...) method. +// They can also be used in traditional Go test functions and even in +// applicaitons. +package assertions + +import ( + "fmt" + "runtime" +) + +// By default we use a no-op serializer. The actual Serializer provides a JSON +// representation of failure results on selected assertions so the goconvey +// web UI can display a convenient diff. +var serializer Serializer = new(noopSerializer) + +// GoConveyMode provides control over JSON serialization of failures. When +// using the assertions in this package from the convey package JSON results +// are very helpful and can be rendered in a DIFF view. In that case, this function +// will be called with a true value to enable the JSON serialization. By default, +// the assertions in this package will not serializer a JSON result, making +// standalone ussage more convenient. +func GoConveyMode(yes bool) { + if yes { + serializer = newSerializer() + } else { + serializer = new(noopSerializer) + } +} + +type testingT interface { + Error(args ...interface{}) +} + +type Assertion struct { + t testingT + failed bool +} + +// New swallows the *testing.T struct and prints failed assertions using t.Error. +// Example: assertions.New(t).So(1, should.Equal, 1) +func New(t testingT) *Assertion { + return &Assertion{t: t} +} + +// Failed reports whether any calls to So (on this Assertion instance) have failed. +func (this *Assertion) Failed() bool { + return this.failed +} + +// So calls the standalone So function and additionally, calls t.Error in failure scenarios. +func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool { + ok, result := So(actual, assert, expected...) + if !ok { + this.failed = true + _, file, line, _ := runtime.Caller(1) + this.t.Error(fmt.Sprintf("\n%s:%d\n%s", file, line, result)) + } + return ok +} + +// So is a convenience function (as opposed to an inconvenience function?) +// for running assertions on arbitrary arguments in any context, be it for testing or even +// application logging. It allows you to perform assertion-like behavior (and get nicely +// formatted messages detailing discrepancies) but without the program blowing up or panicking. +// All that is required is to import this package and call `So` with one of the assertions +// exported by this package as the second parameter. +// The first return parameter is a boolean indicating if the assertion was true. The second +// return parameter is the well-formatted message showing why an assertion was incorrect, or +// blank if the assertion was correct. +// +// Example: +// +// if ok, message := So(x, ShouldBeGreaterThan, y); !ok { +// log.Println(message) +// } +// +func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) { + if result := so(actual, assert, expected...); len(result) == 0 { + return true, result + } else { + return false, result + } +} + +// so is like So, except that it only returns the string message, which is blank if the +// assertion passed. Used to facilitate testing. +func so(actual interface{}, assert func(interface{}, ...interface{}) string, expected ...interface{}) string { + return assert(actual, expected...) +} + +// assertion is an alias for a function with a signature that the So() +// function can handle. Any future or custom assertions should conform to this +// method signature. The return value should be an empty string if the assertion +// passes and a well-formed failure message if not. +type assertion func(actual interface{}, expected ...interface{}) string + +//////////////////////////////////////////////////////////////////////////// diff --git a/vendor/github.com/smartystreets/assertions/doc_test.go b/vendor/github.com/smartystreets/assertions/doc_test.go new file mode 100644 index 0000000..041faaf --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/doc_test.go @@ -0,0 +1,57 @@ +package assertions + +import ( + "bytes" + "fmt" + "testing" +) + +func TestPassingAssertion(t *testing.T) { + fake := &FakeT{buffer: new(bytes.Buffer)} + assertion := New(fake) + passed := assertion.So(1, ShouldEqual, 1) + + if !passed { + t.Error("Assertion failed when it should have passed.") + } + if fake.buffer.Len() > 0 { + t.Error("Unexpected error message was printed.") + } +} + +func TestFailingAssertion(t *testing.T) { + fake := &FakeT{buffer: new(bytes.Buffer)} + assertion := New(fake) + passed := assertion.So(1, ShouldEqual, 2) + + if passed { + t.Error("Assertion passed when it should have failed.") + } + if fake.buffer.Len() == 0 { + t.Error("Expected error message not printed.") + } +} + +func TestFailingGroupsOfAssertions(t *testing.T) { + fake := &FakeT{buffer: new(bytes.Buffer)} + assertion1 := New(fake) + assertion2 := New(fake) + + assertion1.So(1, ShouldEqual, 2) // fail + assertion2.So(1, ShouldEqual, 1) // pass + + if !assertion1.Failed() { + t.Error("Expected the first assertion to have been marked as failed.") + } + if assertion2.Failed() { + t.Error("Expected the second assertion to NOT have been marked as failed.") + } +} + +type FakeT struct { + buffer *bytes.Buffer +} + +func (this *FakeT) Error(args ...interface{}) { + fmt.Fprint(this.buffer, args...) +} diff --git a/vendor/github.com/smartystreets/assertions/equality.go b/vendor/github.com/smartystreets/assertions/equality.go new file mode 100644 index 0000000..3bfe766 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/equality.go @@ -0,0 +1,279 @@ +package assertions + +import ( + "errors" + "fmt" + "math" + "reflect" + "strings" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +// default acceptable delta for ShouldAlmostEqual +const defaultDelta = 0.0000000001 + +// ShouldEqual receives exactly two parameters and does an equality check. +func ShouldEqual(actual interface{}, expected ...interface{}) string { + if message := need(1, expected); message != success { + return message + } + return shouldEqual(actual, expected[0]) +} +func shouldEqual(actual, expected interface{}) (message string) { + defer func() { + if r := recover(); r != nil { + message = serializer.serialize(expected, actual, fmt.Sprintf(shouldHaveBeenEqual, expected, actual)) + return + } + }() + + if matchError := oglematchers.Equals(expected).Matches(actual); matchError != nil { + message = serializer.serialize(expected, actual, fmt.Sprintf(shouldHaveBeenEqual, expected, actual)) + return + } + + return success +} + +// ShouldNotEqual receives exactly two parameters and does an inequality check. +func ShouldNotEqual(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } else if ShouldEqual(actual, expected[0]) == success { + return fmt.Sprintf(shouldNotHaveBeenEqual, actual, expected[0]) + } + return success +} + +// ShouldAlmostEqual makes sure that two parameters are close enough to being equal. +// The acceptable delta may be specified with a third argument, +// or a very small default delta will be used. +func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string { + actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...) + + if err != "" { + return err + } + + if math.Abs(actualFloat-expectedFloat) <= deltaFloat { + return success + } else { + return fmt.Sprintf(shouldHaveBeenAlmostEqual, actualFloat, expectedFloat) + } +} + +// ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual +func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string { + actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...) + + if err != "" { + return err + } + + if math.Abs(actualFloat-expectedFloat) > deltaFloat { + return success + } else { + return fmt.Sprintf(shouldHaveNotBeenAlmostEqual, actualFloat, expectedFloat) + } +} + +func cleanAlmostEqualInput(actual interface{}, expected ...interface{}) (float64, float64, float64, string) { + deltaFloat := 0.0000000001 + + if len(expected) == 0 { + return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided neither)" + } else if len(expected) == 2 { + delta, err := getFloat(expected[1]) + + if err != nil { + return 0.0, 0.0, 0.0, "delta must be a numerical type" + } + + deltaFloat = delta + } else if len(expected) > 2 { + return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided more values)" + } + + actualFloat, err := getFloat(actual) + + if err != nil { + return 0.0, 0.0, 0.0, err.Error() + } + + expectedFloat, err := getFloat(expected[0]) + + if err != nil { + return 0.0, 0.0, 0.0, err.Error() + } + + return actualFloat, expectedFloat, deltaFloat, "" +} + +// returns the float value of any real number, or error if it is not a numerical type +func getFloat(num interface{}) (float64, error) { + numValue := reflect.ValueOf(num) + numKind := numValue.Kind() + + if numKind == reflect.Int || + numKind == reflect.Int8 || + numKind == reflect.Int16 || + numKind == reflect.Int32 || + numKind == reflect.Int64 { + return float64(numValue.Int()), nil + } else if numKind == reflect.Uint || + numKind == reflect.Uint8 || + numKind == reflect.Uint16 || + numKind == reflect.Uint32 || + numKind == reflect.Uint64 { + return float64(numValue.Uint()), nil + } else if numKind == reflect.Float32 || + numKind == reflect.Float64 { + return numValue.Float(), nil + } else { + return 0.0, errors.New("must be a numerical type, but was " + numKind.String()) + } +} + +// ShouldResemble receives exactly two parameters and does a deep equal check (see reflect.DeepEqual) +func ShouldResemble(actual interface{}, expected ...interface{}) string { + if message := need(1, expected); message != success { + return message + } + + if matchError := oglematchers.DeepEquals(expected[0]).Matches(actual); matchError != nil { + expectedSyntax := fmt.Sprintf("%#v", expected[0]) + actualSyntax := fmt.Sprintf("%#v", actual) + var message string + if expectedSyntax == actualSyntax { + message = fmt.Sprintf(shouldHaveResembledTypeMismatch, expected[0], actual, expected[0], actual) + } else { + message = fmt.Sprintf(shouldHaveResembled, expected[0], actual) + } + return serializer.serializeDetailed(expected[0], actual, message) + } + + return success +} + +// ShouldNotResemble receives exactly two parameters and does an inverse deep equal check (see reflect.DeepEqual) +func ShouldNotResemble(actual interface{}, expected ...interface{}) string { + if message := need(1, expected); message != success { + return message + } else if ShouldResemble(actual, expected[0]) == success { + return fmt.Sprintf(shouldNotHaveResembled, actual, expected[0]) + } + return success +} + +// ShouldPointTo receives exactly two parameters and checks to see that they point to the same address. +func ShouldPointTo(actual interface{}, expected ...interface{}) string { + if message := need(1, expected); message != success { + return message + } + return shouldPointTo(actual, expected[0]) + +} +func shouldPointTo(actual, expected interface{}) string { + actualValue := reflect.ValueOf(actual) + expectedValue := reflect.ValueOf(expected) + + if ShouldNotBeNil(actual) != success { + return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "nil") + } else if ShouldNotBeNil(expected) != success { + return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "nil") + } else if actualValue.Kind() != reflect.Ptr { + return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "not") + } else if expectedValue.Kind() != reflect.Ptr { + return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "not") + } else if ShouldEqual(actualValue.Pointer(), expectedValue.Pointer()) != success { + actualAddress := reflect.ValueOf(actual).Pointer() + expectedAddress := reflect.ValueOf(expected).Pointer() + return serializer.serialize(expectedAddress, actualAddress, fmt.Sprintf(shouldHavePointedTo, + actual, actualAddress, + expected, expectedAddress)) + } + return success +} + +// ShouldNotPointTo receives exactly two parameters and checks to see that they point to different addresess. +func ShouldNotPointTo(actual interface{}, expected ...interface{}) string { + if message := need(1, expected); message != success { + return message + } + compare := ShouldPointTo(actual, expected[0]) + if strings.HasPrefix(compare, shouldBePointers) { + return compare + } else if compare == success { + return fmt.Sprintf(shouldNotHavePointedTo, actual, expected[0], reflect.ValueOf(actual).Pointer()) + } + return success +} + +// ShouldBeNil receives a single parameter and ensures that it is nil. +func ShouldBeNil(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } else if actual == nil { + return success + } else if interfaceHasNilValue(actual) { + return success + } + return fmt.Sprintf(shouldHaveBeenNil, actual) +} +func interfaceHasNilValue(actual interface{}) bool { + value := reflect.ValueOf(actual) + kind := value.Kind() + nilable := kind == reflect.Slice || + kind == reflect.Chan || + kind == reflect.Func || + kind == reflect.Ptr || + kind == reflect.Map + + // Careful: reflect.Value.IsNil() will panic unless it's an interface, chan, map, func, slice, or ptr + // Reference: http://golang.org/pkg/reflect/#Value.IsNil + return nilable && value.IsNil() +} + +// ShouldNotBeNil receives a single parameter and ensures that it is not nil. +func ShouldNotBeNil(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } else if ShouldBeNil(actual) == success { + return fmt.Sprintf(shouldNotHaveBeenNil, actual) + } + return success +} + +// ShouldBeTrue receives a single parameter and ensures that it is true. +func ShouldBeTrue(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } else if actual != true { + return fmt.Sprintf(shouldHaveBeenTrue, actual) + } + return success +} + +// ShouldBeFalse receives a single parameter and ensures that it is false. +func ShouldBeFalse(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } else if actual != false { + return fmt.Sprintf(shouldHaveBeenFalse, actual) + } + return success +} + +// ShouldBeZeroValue receives a single parameter and ensures that it is +// the Go equivalent of the default value, or "zero" value. +func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } + zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface() + if !reflect.DeepEqual(zeroVal, actual) { + return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldHaveBeenZeroValue, actual)) + } + return success +} diff --git a/vendor/github.com/smartystreets/assertions/equality_test.go b/vendor/github.com/smartystreets/assertions/equality_test.go new file mode 100644 index 0000000..ee39397 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/equality_test.go @@ -0,0 +1,267 @@ +package assertions + +import ( + "fmt" + "reflect" + "testing" +) + +func TestShouldEqual(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so(1, ShouldEqual), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldEqual, 1, 2), "This assertion requires exactly 1 comparison values (you provided 2).") + fail(t, so(1, ShouldEqual, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + pass(t, so(1, ShouldEqual, 1)) + fail(t, so(1, ShouldEqual, 2), "2|1|Expected: '2' Actual: '1' (Should be equal)") + + pass(t, so(true, ShouldEqual, true)) + fail(t, so(true, ShouldEqual, false), "false|true|Expected: 'false' Actual: 'true' (Should be equal)") + + pass(t, so("hi", ShouldEqual, "hi")) + fail(t, so("hi", ShouldEqual, "bye"), "bye|hi|Expected: 'bye' Actual: 'hi' (Should be equal)") + + pass(t, so(42, ShouldEqual, uint(42))) + + fail(t, so(Thing1{"hi"}, ShouldEqual, Thing1{}), "{}|{hi}|Expected: '{}' Actual: '{hi}' (Should be equal)") + fail(t, so(Thing1{"hi"}, ShouldEqual, Thing1{"hi"}), "{hi}|{hi}|Expected: '{hi}' Actual: '{hi}' (Should be equal)") + fail(t, so(&Thing1{"hi"}, ShouldEqual, &Thing1{"hi"}), "&{hi}|&{hi}|Expected: '&{hi}' Actual: '&{hi}' (Should be equal)") + + fail(t, so(Thing1{}, ShouldEqual, Thing2{}), "{}|{}|Expected: '{}' Actual: '{}' (Should be equal)") +} + +func TestShouldNotEqual(t *testing.T) { + fail(t, so(1, ShouldNotEqual), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldNotEqual, 1, 2), "This assertion requires exactly 1 comparison values (you provided 2).") + fail(t, so(1, ShouldNotEqual, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + pass(t, so(1, ShouldNotEqual, 2)) + fail(t, so(1, ShouldNotEqual, 1), "Expected '1' to NOT equal '1' (but it did)!") + + pass(t, so(true, ShouldNotEqual, false)) + fail(t, so(true, ShouldNotEqual, true), "Expected 'true' to NOT equal 'true' (but it did)!") + + pass(t, so("hi", ShouldNotEqual, "bye")) + fail(t, so("hi", ShouldNotEqual, "hi"), "Expected 'hi' to NOT equal 'hi' (but it did)!") + + pass(t, so(&Thing1{"hi"}, ShouldNotEqual, &Thing1{"hi"})) + pass(t, so(Thing1{"hi"}, ShouldNotEqual, Thing1{"hi"})) + pass(t, so(Thing1{}, ShouldNotEqual, Thing1{})) + pass(t, so(Thing1{}, ShouldNotEqual, Thing2{})) +} + +func TestShouldAlmostEqual(t *testing.T) { + fail(t, so(1, ShouldAlmostEqual), "This assertion requires exactly one comparison value and an optional delta (you provided neither)") + fail(t, so(1, ShouldAlmostEqual, 1, 2, 3), "This assertion requires exactly one comparison value and an optional delta (you provided more values)") + + // with the default delta + pass(t, so(1, ShouldAlmostEqual, .99999999999999)) + pass(t, so(1.3612499999999996, ShouldAlmostEqual, 1.36125)) + pass(t, so(0.7285312499999999, ShouldAlmostEqual, 0.72853125)) + fail(t, so(1, ShouldAlmostEqual, .99), "Expected '1' to almost equal '0.99' (but it didn't)!") + + // with a different delta + pass(t, so(100.0, ShouldAlmostEqual, 110.0, 10.0)) + fail(t, so(100.0, ShouldAlmostEqual, 111.0, 10.5), "Expected '100' to almost equal '111' (but it didn't)!") + + // ints should work + pass(t, so(100, ShouldAlmostEqual, 100.0)) + fail(t, so(100, ShouldAlmostEqual, 99.0), "Expected '100' to almost equal '99' (but it didn't)!") + + // float32 should work + pass(t, so(float64(100.0), ShouldAlmostEqual, float32(100.0))) + fail(t, so(float32(100.0), ShouldAlmostEqual, 99.0, float32(0.1)), "Expected '100' to almost equal '99' (but it didn't)!") +} + +func TestShouldNotAlmostEqual(t *testing.T) { + fail(t, so(1, ShouldNotAlmostEqual), "This assertion requires exactly one comparison value and an optional delta (you provided neither)") + fail(t, so(1, ShouldNotAlmostEqual, 1, 2, 3), "This assertion requires exactly one comparison value and an optional delta (you provided more values)") + + // with the default delta + fail(t, so(1, ShouldNotAlmostEqual, .99999999999999), "Expected '1' to NOT almost equal '0.99999999999999' (but it did)!") + fail(t, so(1.3612499999999996, ShouldNotAlmostEqual, 1.36125), "Expected '1.3612499999999996' to NOT almost equal '1.36125' (but it did)!") + pass(t, so(1, ShouldNotAlmostEqual, .99)) + + // with a different delta + fail(t, so(100.0, ShouldNotAlmostEqual, 110.0, 10.0), "Expected '100' to NOT almost equal '110' (but it did)!") + pass(t, so(100.0, ShouldNotAlmostEqual, 111.0, 10.5)) + + // ints should work + fail(t, so(100, ShouldNotAlmostEqual, 100.0), "Expected '100' to NOT almost equal '100' (but it did)!") + pass(t, so(100, ShouldNotAlmostEqual, 99.0)) + + // float32 should work + fail(t, so(float64(100.0), ShouldNotAlmostEqual, float32(100.0)), "Expected '100' to NOT almost equal '100' (but it did)!") + pass(t, so(float32(100.0), ShouldNotAlmostEqual, 99.0, float32(0.1))) +} + +func TestShouldResemble(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so(Thing1{"hi"}, ShouldResemble), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(Thing1{"hi"}, ShouldResemble, Thing1{"hi"}, Thing1{"hi"}), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(Thing1{"hi"}, ShouldResemble, Thing1{"hi"})) + fail(t, so(Thing1{"hi"}, ShouldResemble, Thing1{"bye"}), "{bye}|{hi}|Expected: 'assertions.Thing1{a:\"bye\"}' Actual: 'assertions.Thing1{a:\"hi\"}' (Should resemble)!") + + var ( + a []int + b []int = []int{} + ) + + fail(t, so(a, ShouldResemble, b), "[]|[]|Expected: '[]int{}' Actual: '[]int(nil)' (Should resemble)!") + fail(t, so(2, ShouldResemble, 1), "1|2|Expected: '1' Actual: '2' (Should resemble)!") + + fail(t, so(StringStringMapAlias{"hi": "bye"}, ShouldResemble, map[string]string{"hi": "bye"}), + "map[hi:bye]|map[hi:bye]|Expected: 'map[string]string{\"hi\":\"bye\"}' Actual: 'assertions.StringStringMapAlias{\"hi\":\"bye\"}' (Should resemble)!") + fail(t, so(StringSliceAlias{"hi", "bye"}, ShouldResemble, []string{"hi", "bye"}), + "[hi bye]|[hi bye]|Expected: '[]string{\"hi\", \"bye\"}' Actual: 'assertions.StringSliceAlias{\"hi\", \"bye\"}' (Should resemble)!") + + // some types come out looking the same when represented with "%#v" so we show type mismatch info: + fail(t, so(StringAlias("hi"), ShouldResemble, "hi"), "hi|hi|Expected: '\"hi\"' Actual: '\"hi\"' (Type mismatch: 'string' vs 'assertions.StringAlias')!") + fail(t, so(IntAlias(42), ShouldResemble, 42), "42|42|Expected: '42' Actual: '42' (Type mismatch: 'int' vs 'assertions.IntAlias')!") +} + +func TestShouldNotResemble(t *testing.T) { + fail(t, so(Thing1{"hi"}, ShouldNotResemble), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(Thing1{"hi"}, ShouldNotResemble, Thing1{"hi"}, Thing1{"hi"}), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(Thing1{"hi"}, ShouldNotResemble, Thing1{"bye"})) + fail(t, so(Thing1{"hi"}, ShouldNotResemble, Thing1{"hi"}), + "Expected 'assertions.Thing1{a:\"hi\"}' to NOT resemble 'assertions.Thing1{a:\"hi\"}' (but it did)!") + + pass(t, so(map[string]string{"hi": "bye"}, ShouldResemble, map[string]string{"hi": "bye"})) + pass(t, so(IntAlias(42), ShouldNotResemble, 42)) + + pass(t, so(StringSliceAlias{"hi", "bye"}, ShouldNotResemble, []string{"hi", "bye"})) +} + +func TestShouldPointTo(t *testing.T) { + serializer = newFakeSerializer() + + t1 := &Thing1{} + t2 := t1 + t3 := &Thing1{} + + pointer1 := reflect.ValueOf(t1).Pointer() + pointer3 := reflect.ValueOf(t3).Pointer() + + fail(t, so(t1, ShouldPointTo), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(t1, ShouldPointTo, t2, t3), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(t1, ShouldPointTo, t2)) + fail(t, so(t1, ShouldPointTo, t3), fmt.Sprintf( + "%v|%v|Expected '&{a:}' (address: '%v') and '&{a:}' (address: '%v') to be the same address (but their weren't)!", + pointer3, pointer1, pointer1, pointer3)) + + t4 := Thing1{} + t5 := t4 + + fail(t, so(t4, ShouldPointTo, t5), "Both arguments should be pointers (the first was not)!") + fail(t, so(&t4, ShouldPointTo, t5), "Both arguments should be pointers (the second was not)!") + fail(t, so(nil, ShouldPointTo, nil), "Both arguments should be pointers (the first was nil)!") + fail(t, so(&t4, ShouldPointTo, nil), "Both arguments should be pointers (the second was nil)!") +} + +func TestShouldNotPointTo(t *testing.T) { + t1 := &Thing1{} + t2 := t1 + t3 := &Thing1{} + + pointer1 := reflect.ValueOf(t1).Pointer() + + fail(t, so(t1, ShouldNotPointTo), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(t1, ShouldNotPointTo, t2, t3), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(t1, ShouldNotPointTo, t3)) + fail(t, so(t1, ShouldNotPointTo, t2), fmt.Sprintf("Expected '&{a:}' and '&{a:}' to be different references (but they matched: '%v')!", pointer1)) + + t4 := Thing1{} + t5 := t4 + + fail(t, so(t4, ShouldNotPointTo, t5), "Both arguments should be pointers (the first was not)!") + fail(t, so(&t4, ShouldNotPointTo, t5), "Both arguments should be pointers (the second was not)!") + fail(t, so(nil, ShouldNotPointTo, nil), "Both arguments should be pointers (the first was nil)!") + fail(t, so(&t4, ShouldNotPointTo, nil), "Both arguments should be pointers (the second was nil)!") +} + +func TestShouldBeNil(t *testing.T) { + fail(t, so(nil, ShouldBeNil, nil, nil, nil), "This assertion requires exactly 0 comparison values (you provided 3).") + fail(t, so(nil, ShouldBeNil, nil), "This assertion requires exactly 0 comparison values (you provided 1).") + + pass(t, so(nil, ShouldBeNil)) + fail(t, so(1, ShouldBeNil), "Expected: nil Actual: '1'") + + var thing Thinger + pass(t, so(thing, ShouldBeNil)) + thing = &Thing{} + fail(t, so(thing, ShouldBeNil), "Expected: nil Actual: '&{}'") + + var thingOne *Thing1 + pass(t, so(thingOne, ShouldBeNil)) + + var nilSlice []int = nil + pass(t, so(nilSlice, ShouldBeNil)) + + var nilMap map[string]string = nil + pass(t, so(nilMap, ShouldBeNil)) + + var nilChannel chan int = nil + pass(t, so(nilChannel, ShouldBeNil)) + + var nilFunc func() = nil + pass(t, so(nilFunc, ShouldBeNil)) + + var nilInterface interface{} = nil + pass(t, so(nilInterface, ShouldBeNil)) +} + +func TestShouldNotBeNil(t *testing.T) { + fail(t, so(nil, ShouldNotBeNil, nil, nil, nil), "This assertion requires exactly 0 comparison values (you provided 3).") + fail(t, so(nil, ShouldNotBeNil, nil), "This assertion requires exactly 0 comparison values (you provided 1).") + + fail(t, so(nil, ShouldNotBeNil), "Expected '' to NOT be nil (but it was)!") + pass(t, so(1, ShouldNotBeNil)) + + var thing Thinger + fail(t, so(thing, ShouldNotBeNil), "Expected '' to NOT be nil (but it was)!") + thing = &Thing{} + pass(t, so(thing, ShouldNotBeNil)) +} + +func TestShouldBeTrue(t *testing.T) { + fail(t, so(true, ShouldBeTrue, 1, 2, 3), "This assertion requires exactly 0 comparison values (you provided 3).") + fail(t, so(true, ShouldBeTrue, 1), "This assertion requires exactly 0 comparison values (you provided 1).") + + fail(t, so(false, ShouldBeTrue), "Expected: true Actual: false") + fail(t, so(1, ShouldBeTrue), "Expected: true Actual: 1") + pass(t, so(true, ShouldBeTrue)) +} + +func TestShouldBeFalse(t *testing.T) { + fail(t, so(false, ShouldBeFalse, 1, 2, 3), "This assertion requires exactly 0 comparison values (you provided 3).") + fail(t, so(false, ShouldBeFalse, 1), "This assertion requires exactly 0 comparison values (you provided 1).") + + fail(t, so(true, ShouldBeFalse), "Expected: false Actual: true") + fail(t, so(1, ShouldBeFalse), "Expected: false Actual: 1") + pass(t, so(false, ShouldBeFalse)) +} + +func TestShouldBeZeroValue(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so(0, ShouldBeZeroValue, 1, 2, 3), "This assertion requires exactly 0 comparison values (you provided 3).") + fail(t, so(false, ShouldBeZeroValue, true), "This assertion requires exactly 0 comparison values (you provided 1).") + + fail(t, so(1, ShouldBeZeroValue), "0|1|'1' should have been the zero value") //"Expected: (zero value) Actual: 1") + fail(t, so(true, ShouldBeZeroValue), "false|true|'true' should have been the zero value") //"Expected: (zero value) Actual: true") + fail(t, so("123", ShouldBeZeroValue), "|123|'123' should have been the zero value") //"Expected: (zero value) Actual: 123") + fail(t, so(" ", ShouldBeZeroValue), "| |' ' should have been the zero value") //"Expected: (zero value) Actual: ") + fail(t, so([]string{"Nonempty"}, ShouldBeZeroValue), "[]|[Nonempty]|'[Nonempty]' should have been the zero value") //"Expected: (zero value) Actual: [Nonempty]") + fail(t, so(struct{ a string }{a: "asdf"}, ShouldBeZeroValue), "{}|{asdf}|'{a:asdf}' should have been the zero value") + pass(t, so(0, ShouldBeZeroValue)) + pass(t, so(false, ShouldBeZeroValue)) + pass(t, so("", ShouldBeZeroValue)) + pass(t, so(struct{}{}, ShouldBeZeroValue)) +} diff --git a/vendor/github.com/smartystreets/assertions/filter.go b/vendor/github.com/smartystreets/assertions/filter.go new file mode 100644 index 0000000..ee368a9 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/filter.go @@ -0,0 +1,23 @@ +package assertions + +import "fmt" + +const ( + success = "" + needExactValues = "This assertion requires exactly %d comparison values (you provided %d)." + needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)." +) + +func need(needed int, expected []interface{}) string { + if len(expected) != needed { + return fmt.Sprintf(needExactValues, needed, len(expected)) + } + return success +} + +func atLeast(minimum int, expected []interface{}) string { + if len(expected) < 1 { + return needNonEmptyCollection + } + return success +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE b/vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md b/vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md new file mode 100644 index 0000000..215a2bb --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md @@ -0,0 +1,58 @@ +[![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers) + +`oglematchers` is a package for the Go programming language containing a set of +matchers, useful in a testing or mocking framework, inspired by and mostly +compatible with [Google Test][googletest] for C++ and +[Google JS Test][google-js-test]. The package is used by the +[ogletest][ogletest] testing framework and [oglemock][oglemock] mocking +framework, which may be more directly useful to you, but can be generically used +elsewhere as well. + +A "matcher" is simply an object with a `Matches` method defining a set of golang +values matched by the matcher, and a `Description` method describing that set. +For example, here are some matchers: + +```go +// Numbers +Equals(17.13) +LessThan(19) + +// Strings +Equals("taco") +HasSubstr("burrito") +MatchesRegex("t.*o") + +// Combining matchers +AnyOf(LessThan(17), GreaterThan(19)) +``` + +There are lots more; see [here][reference] for a reference. You can also add +your own simply by implementing the `oglematchers.Matcher` interface. + + +Installation +------------ + +First, make sure you have installed Go 1.0.2 or newer. See +[here][golang-install] for instructions. + +Use the following command to install `oglematchers` and keep it up to date: + + go get -u github.com/smartystreets/assertions/internal/oglematchers + + +Documentation +------------- + +See [here][reference] for documentation. Alternatively, you can install the +package and then use `godoc`: + + godoc github.com/smartystreets/assertions/internal/oglematchers + + +[reference]: http://godoc.org/github.com/smartystreets/assertions/internal/oglematchers +[golang-install]: http://golang.org/doc/install.html +[googletest]: http://code.google.com/p/googletest/ +[google-js-test]: http://code.google.com/p/google-js-test/ +[ogletest]: http://github.com/smartystreets/assertions/internal/ogletest +[oglemock]: http://github.com/smartystreets/assertions/internal/oglemock diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of.go new file mode 100644 index 0000000..d93a974 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of.go @@ -0,0 +1,70 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "strings" +) + +// AllOf accepts a set of matchers S and returns a matcher that follows the +// algorithm below when considering a candidate c: +// +// 1. Return true if for every Matcher m in S, m matches c. +// +// 2. Otherwise, if there is a matcher m in S such that m returns a fatal +// error for c, return that matcher's error message. +// +// 3. Otherwise, return false with the error from some wrapped matcher. +// +// This is akin to a logical AND operation for matchers. +func AllOf(matchers ...Matcher) Matcher { + return &allOfMatcher{matchers} +} + +type allOfMatcher struct { + wrappedMatchers []Matcher +} + +func (m *allOfMatcher) Description() string { + // Special case: the empty set. + if len(m.wrappedMatchers) == 0 { + return "is anything" + } + + // Join the descriptions for the wrapped matchers. + wrappedDescs := make([]string, len(m.wrappedMatchers)) + for i, wrappedMatcher := range m.wrappedMatchers { + wrappedDescs[i] = wrappedMatcher.Description() + } + + return strings.Join(wrappedDescs, ", and ") +} + +func (m *allOfMatcher) Matches(c interface{}) (err error) { + for _, wrappedMatcher := range m.wrappedMatchers { + if wrappedErr := wrappedMatcher.Matches(c); wrappedErr != nil { + err = wrappedErr + + // If the error is fatal, return immediately with this error. + _, ok := wrappedErr.(*FatalError) + if ok { + return + } + } + } + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of_test.go new file mode 100644 index 0000000..89ceb69 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/all_of_test.go @@ -0,0 +1,110 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "errors" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type allOfFakeMatcher struct { + desc string + err error +} + +func (m *allOfFakeMatcher) Matches(c interface{}) error { + return m.err +} + +func (m *allOfFakeMatcher) Description() string { + return m.desc +} + +type AllOfTest struct { +} + +func init() { RegisterTestSuite(&AllOfTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *AllOfTest) DescriptionWithEmptySet() { + m := AllOf() + ExpectEq("is anything", m.Description()) +} + +func (t *AllOfTest) DescriptionWithOneMatcher() { + m := AllOf(&allOfFakeMatcher{"taco", errors.New("")}) + ExpectEq("taco", m.Description()) +} + +func (t *AllOfTest) DescriptionWithMultipleMatchers() { + m := AllOf( + &allOfFakeMatcher{"taco", errors.New("")}, + &allOfFakeMatcher{"burrito", errors.New("")}, + &allOfFakeMatcher{"enchilada", errors.New("")}) + + ExpectEq("taco, and burrito, and enchilada", m.Description()) +} + +func (t *AllOfTest) EmptySet() { + m := AllOf() + err := m.Matches(17) + + ExpectEq(nil, err) +} + +func (t *AllOfTest) OneMatcherReturnsFatalErrorAndSomeOthersFail() { + m := AllOf( + &allOfFakeMatcher{"", errors.New("")}, + &allOfFakeMatcher{"", NewFatalError("taco")}, + &allOfFakeMatcher{"", errors.New("")}, + &allOfFakeMatcher{"", nil}) + + err := m.Matches(17) + + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("taco"))) +} + +func (t *AllOfTest) OneMatcherReturnsNonFatalAndOthersSayTrue() { + m := AllOf( + &allOfFakeMatcher{"", nil}, + &allOfFakeMatcher{"", errors.New("taco")}, + &allOfFakeMatcher{"", nil}) + + err := m.Matches(17) + + ExpectFalse(isFatal(err)) + ExpectThat(err, Error(Equals("taco"))) +} + +func (t *AllOfTest) AllMatchersSayTrue() { + m := AllOf( + &allOfFakeMatcher{"", nil}, + &allOfFakeMatcher{"", nil}, + &allOfFakeMatcher{"", nil}) + + err := m.Matches(17) + + ExpectEq(nil, err) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/any.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any.go new file mode 100644 index 0000000..f6991ec --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any.go @@ -0,0 +1,32 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +// Any returns a matcher that matches any value. +func Any() Matcher { + return &anyMatcher{} +} + +type anyMatcher struct { +} + +func (m *anyMatcher) Description() string { + return "is anything" +} + +func (m *anyMatcher) Matches(c interface{}) error { + return nil +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go new file mode 100644 index 0000000..2918b51 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go @@ -0,0 +1,94 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +// AnyOf accepts a set of values S and returns a matcher that follows the +// algorithm below when considering a candidate c: +// +// 1. If there exists a value m in S such that m implements the Matcher +// interface and m matches c, return true. +// +// 2. Otherwise, if there exists a value v in S such that v does not implement +// the Matcher interface and the matcher Equals(v) matches c, return true. +// +// 3. Otherwise, if there is a value m in S such that m implements the Matcher +// interface and m returns a fatal error for c, return that fatal error. +// +// 4. Otherwise, return false. +// +// This is akin to a logical OR operation for matchers, with non-matchers x +// being treated as Equals(x). +func AnyOf(vals ...interface{}) Matcher { + // Get ahold of a type variable for the Matcher interface. + var dummy *Matcher + matcherType := reflect.TypeOf(dummy).Elem() + + // Create a matcher for each value, or use the value itself if it's already a + // matcher. + wrapped := make([]Matcher, len(vals)) + for i, v := range vals { + t := reflect.TypeOf(v) + if t != nil && t.Implements(matcherType) { + wrapped[i] = v.(Matcher) + } else { + wrapped[i] = Equals(v) + } + } + + return &anyOfMatcher{wrapped} +} + +type anyOfMatcher struct { + wrapped []Matcher +} + +func (m *anyOfMatcher) Description() string { + wrappedDescs := make([]string, len(m.wrapped)) + for i, matcher := range m.wrapped { + wrappedDescs[i] = matcher.Description() + } + + return fmt.Sprintf("or(%s)", strings.Join(wrappedDescs, ", ")) +} + +func (m *anyOfMatcher) Matches(c interface{}) (err error) { + err = errors.New("") + + // Try each matcher in turn. + for _, matcher := range m.wrapped { + wrappedErr := matcher.Matches(c) + + // Return immediately if there's a match. + if wrappedErr == nil { + err = nil + return + } + + // Note the fatal error, if any. + if _, isFatal := wrappedErr.(*FatalError); isFatal { + err = wrappedErr + } + } + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of_test.go new file mode 100644 index 0000000..5700070 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of_test.go @@ -0,0 +1,139 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "errors" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type fakeAnyOfMatcher struct { + desc string + err error +} + +func (m *fakeAnyOfMatcher) Matches(c interface{}) error { + return m.err +} + +func (m *fakeAnyOfMatcher) Description() string { + return m.desc +} + +type AnyOfTest struct { +} + +func init() { RegisterTestSuite(&AnyOfTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *AnyOfTest) EmptySet() { + matcher := AnyOf() + + err := matcher.Matches(0) + ExpectThat(err, Error(Equals(""))) +} + +func (t *AnyOfTest) OneTrue() { + matcher := AnyOf( + &fakeAnyOfMatcher{"", NewFatalError("foo")}, + 17, + &fakeAnyOfMatcher{"", errors.New("foo")}, + &fakeAnyOfMatcher{"", nil}, + &fakeAnyOfMatcher{"", errors.New("foo")}, + ) + + err := matcher.Matches(0) + ExpectEq(nil, err) +} + +func (t *AnyOfTest) OneEqual() { + matcher := AnyOf( + &fakeAnyOfMatcher{"", NewFatalError("foo")}, + &fakeAnyOfMatcher{"", errors.New("foo")}, + 13, + "taco", + 19, + &fakeAnyOfMatcher{"", errors.New("foo")}, + ) + + err := matcher.Matches("taco") + ExpectEq(nil, err) +} + +func (t *AnyOfTest) OneFatal() { + matcher := AnyOf( + &fakeAnyOfMatcher{"", errors.New("foo")}, + 17, + &fakeAnyOfMatcher{"", NewFatalError("taco")}, + &fakeAnyOfMatcher{"", errors.New("foo")}, + ) + + err := matcher.Matches(0) + ExpectThat(err, Error(Equals("taco"))) +} + +func (t *AnyOfTest) OneNil() { + var err error + matcher := AnyOf( + 13, + nil, + 19, + ) + + // No match + err = matcher.Matches(14) + ExpectNe(nil, err) + + // Match + err = matcher.Matches(nil) + ExpectEq(nil, err) +} + +func (t *AnyOfTest) AllFalseAndNotEqual() { + matcher := AnyOf( + &fakeAnyOfMatcher{"", errors.New("foo")}, + 17, + &fakeAnyOfMatcher{"", errors.New("foo")}, + 19, + ) + + err := matcher.Matches(0) + ExpectThat(err, Error(Equals(""))) +} + +func (t *AnyOfTest) DescriptionForEmptySet() { + matcher := AnyOf() + ExpectEq("or()", matcher.Description()) +} + +func (t *AnyOfTest) DescriptionForNonEmptySet() { + matcher := AnyOf( + &fakeAnyOfMatcher{"taco", nil}, + "burrito", + &fakeAnyOfMatcher{"enchilada", nil}, + ) + + ExpectEq("or(taco, burrito, enchilada)", matcher.Description()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_test.go new file mode 100644 index 0000000..70d7e64 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_test.go @@ -0,0 +1,53 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type AnyTest struct { +} + +func init() { RegisterTestSuite(&AnyTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *AnyTest) Description() { + m := Any() + ExpectEq("is anything", m.Description()) +} + +func (t *AnyTest) Matches() { + var err error + m := Any() + + err = m.Matches(nil) + ExpectEq(nil, err) + + err = m.Matches(17) + ExpectEq(nil, err) + + err = m.Matches("taco") + ExpectEq(nil, err) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go new file mode 100644 index 0000000..2f326db --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go @@ -0,0 +1,61 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "fmt" + "reflect" +) + +// Return a matcher that matches arrays slices with at least one element that +// matches the supplied argument. If the argument x is not itself a Matcher, +// this is equivalent to Contains(Equals(x)). +func Contains(x interface{}) Matcher { + var result containsMatcher + var ok bool + + if result.elementMatcher, ok = x.(Matcher); !ok { + result.elementMatcher = Equals(x) + } + + return &result +} + +type containsMatcher struct { + elementMatcher Matcher +} + +func (m *containsMatcher) Description() string { + return fmt.Sprintf("contains: %s", m.elementMatcher.Description()) +} + +func (m *containsMatcher) Matches(candidate interface{}) error { + // The candidate must be a slice or an array. + v := reflect.ValueOf(candidate) + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + return NewFatalError("which is not a slice or array") + } + + // Check each element. + for i := 0; i < v.Len(); i++ { + elem := v.Index(i) + if matchErr := m.elementMatcher.Matches(elem.Interface()); matchErr == nil { + return nil + } + } + + return fmt.Errorf("") +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains_test.go new file mode 100644 index 0000000..772eb1d --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains_test.go @@ -0,0 +1,234 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type ContainsTest struct{} + +func init() { RegisterTestSuite(&ContainsTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *ContainsTest) WrongTypeCandidates() { + m := Contains("") + ExpectEq("contains: ", m.Description()) + + var err error + + // Nil candidate + err = m.Matches(nil) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("array"))) + ExpectThat(err, Error(HasSubstr("slice"))) + + // String candidate + err = m.Matches("") + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("array"))) + ExpectThat(err, Error(HasSubstr("slice"))) + + // Map candidate + err = m.Matches(make(map[string]string)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("array"))) + ExpectThat(err, Error(HasSubstr("slice"))) +} + +func (t *ContainsTest) NilArgument() { + m := Contains(nil) + ExpectEq("contains: is nil", m.Description()) + + var c interface{} + var err error + + // Empty array of pointers + c = [...]*int{} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Empty slice of pointers + c = []*int{} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-empty array of integers + c = [...]int{17, 0, 19} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-empty slice of integers + c = []int{17, 0, 19} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching array of pointers + c = [...]*int{new(int), new(int)} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching slice of pointers + c = []*int{new(int), new(int)} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Matching array of pointers + c = [...]*int{new(int), nil, new(int)} + err = m.Matches(c) + ExpectEq(nil, err) + + // Matching slice of pointers + c = []*int{new(int), nil, new(int)} + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching slice of pointers from matching array + someArray := [...]*int{new(int), nil, new(int)} + c = someArray[0:1] + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +func (t *ContainsTest) StringArgument() { + m := Contains("taco") + ExpectEq("contains: taco", m.Description()) + + var c interface{} + var err error + + // Non-matching array of strings + c = [...]string{"burrito", "enchilada"} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching slice of strings + c = []string{"burrito", "enchilada"} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Matching array of strings + c = [...]string{"burrito", "taco", "enchilada"} + err = m.Matches(c) + ExpectEq(nil, err) + + // Matching slice of strings + c = []string{"burrito", "taco", "enchilada"} + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching slice of strings from matching array + someArray := [...]string{"burrito", "taco", "enchilada"} + c = someArray[0:1] + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +func (t *ContainsTest) IntegerArgument() { + m := Contains(int(17)) + ExpectEq("contains: 17", m.Description()) + + var c interface{} + var err error + + // Non-matching array of integers + c = [...]int{13, 19} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching slice of integers + c = []int{13, 19} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Matching array of integers + c = [...]int{13, 17, 19} + err = m.Matches(c) + ExpectEq(nil, err) + + // Matching slice of integers + c = []int{13, 17, 19} + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching slice of integers from matching array + someArray := [...]int{13, 17, 19} + c = someArray[0:1] + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching array of floats + c = [...]float32{13, 17.5, 19} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching slice of floats + c = []float32{13, 17.5, 19} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Matching array of floats + c = [...]float32{13, 17, 19} + err = m.Matches(c) + ExpectEq(nil, err) + + // Matching slice of floats + c = []float32{13, 17, 19} + err = m.Matches(c) + ExpectEq(nil, err) +} + +func (t *ContainsTest) MatcherArgument() { + m := Contains(HasSubstr("ac")) + ExpectEq("contains: has substring \"ac\"", m.Description()) + + var c interface{} + var err error + + // Non-matching array of strings + c = [...]string{"burrito", "enchilada"} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Non-matching slice of strings + c = []string{"burrito", "enchilada"} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Matching array of strings + c = [...]string{"burrito", "taco", "enchilada"} + err = m.Matches(c) + ExpectEq(nil, err) + + // Matching slice of strings + c = []string{"burrito", "taco", "enchilada"} + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching slice of strings from matching array + someArray := [...]string{"burrito", "taco", "enchilada"} + c = someArray[0:1] + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go new file mode 100644 index 0000000..1d91bae --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go @@ -0,0 +1,88 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "bytes" + "errors" + "fmt" + "reflect" +) + +var byteSliceType reflect.Type = reflect.TypeOf([]byte{}) + +// DeepEquals returns a matcher that matches based on 'deep equality', as +// defined by the reflect package. This matcher requires that values have +// identical types to x. +func DeepEquals(x interface{}) Matcher { + return &deepEqualsMatcher{x} +} + +type deepEqualsMatcher struct { + x interface{} +} + +func (m *deepEqualsMatcher) Description() string { + xDesc := fmt.Sprintf("%v", m.x) + xValue := reflect.ValueOf(m.x) + + // Special case: fmt.Sprintf presents nil slices as "[]", but + // reflect.DeepEqual makes a distinction between nil and empty slices. Make + // this less confusing. + if xValue.Kind() == reflect.Slice && xValue.IsNil() { + xDesc = "" + } + + return fmt.Sprintf("deep equals: %s", xDesc) +} + +func (m *deepEqualsMatcher) Matches(c interface{}) error { + // Make sure the types match. + ct := reflect.TypeOf(c) + xt := reflect.TypeOf(m.x) + + if ct != xt { + return NewFatalError(fmt.Sprintf("which is of type %v", ct)) + } + + // Special case: handle byte slices more efficiently. + cValue := reflect.ValueOf(c) + xValue := reflect.ValueOf(m.x) + + if ct == byteSliceType && !cValue.IsNil() && !xValue.IsNil() { + xBytes := m.x.([]byte) + cBytes := c.([]byte) + + if bytes.Equal(cBytes, xBytes) { + return nil + } + + return errors.New("") + } + + // Defer to the reflect package. + if reflect.DeepEqual(m.x, c) { + return nil + } + + // Special case: if the comparison failed because c is the nil slice, given + // an indication of this (since its value is printed as "[]"). + if cValue.Kind() == reflect.Slice && cValue.IsNil() { + return errors.New("which is nil") + } + + return errors.New("") +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals_test.go new file mode 100644 index 0000000..40aa83f --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals_test.go @@ -0,0 +1,344 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "bytes" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "testing" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type DeepEqualsTest struct{} + +func init() { RegisterTestSuite(&DeepEqualsTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *DeepEqualsTest) WrongTypeCandidateWithScalarValue() { + var x int = 17 + m := DeepEquals(x) + + var err error + + // Nil candidate. + err = m.Matches(nil) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr(""))) + + // Int alias candidate. + type intAlias int + err = m.Matches(intAlias(x)) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("intAlias"))) + + // String candidate. + err = m.Matches("taco") + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("string"))) + + // Byte slice candidate. + err = m.Matches([]byte{}) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint8"))) + + // Other slice candidate. + err = m.Matches([]uint16{}) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint16"))) + + // Unsigned int candidate. + err = m.Matches(uint(17)) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("uint"))) +} + +func (t *DeepEqualsTest) WrongTypeCandidateWithByteSliceValue() { + x := []byte{} + m := DeepEquals(x) + + var err error + + // Nil candidate. + err = m.Matches(nil) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr(""))) + + // String candidate. + err = m.Matches("taco") + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("string"))) + + // Slice candidate with wrong value type. + err = m.Matches([]uint16{}) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint16"))) +} + +func (t *DeepEqualsTest) WrongTypeCandidateWithOtherSliceValue() { + x := []uint16{} + m := DeepEquals(x) + + var err error + + // Nil candidate. + err = m.Matches(nil) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr(""))) + + // String candidate. + err = m.Matches("taco") + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("string"))) + + // Byte slice candidate with wrong value type. + err = m.Matches([]byte{}) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint8"))) + + // Other slice candidate with wrong value type. + err = m.Matches([]uint32{}) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint32"))) +} + +func (t *DeepEqualsTest) WrongTypeCandidateWithNilLiteralValue() { + m := DeepEquals(nil) + + var err error + + // String candidate. + err = m.Matches("taco") + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("string"))) + + // Nil byte slice candidate. + err = m.Matches([]byte(nil)) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint8"))) + + // Nil other slice candidate. + err = m.Matches([]uint16(nil)) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("type"))) + ExpectThat(err, Error(HasSubstr("[]uint16"))) +} + +func (t *DeepEqualsTest) NilLiteralValue() { + m := DeepEquals(nil) + ExpectEq("deep equals: ", m.Description()) + + var c interface{} + var err error + + // Nil literal candidate. + c = nil + err = m.Matches(c) + ExpectEq(nil, err) +} + +func (t *DeepEqualsTest) IntValue() { + m := DeepEquals(int(17)) + ExpectEq("deep equals: 17", m.Description()) + + var c interface{} + var err error + + // Matching int. + c = int(17) + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching int. + c = int(18) + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +func (t *DeepEqualsTest) ByteSliceValue() { + x := []byte{17, 19} + m := DeepEquals(x) + ExpectEq("deep equals: [17 19]", m.Description()) + + var c []byte + var err error + + // Matching. + c = make([]byte, len(x)) + AssertEq(len(x), copy(c, x)) + + err = m.Matches(c) + ExpectEq(nil, err) + + // Nil slice. + c = []byte(nil) + err = m.Matches(c) + ExpectThat(err, Error(Equals("which is nil"))) + + // Prefix. + AssertGt(len(x), 1) + c = make([]byte, len(x)-1) + AssertEq(len(x)-1, copy(c, x)) + + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Suffix. + c = make([]byte, len(x)+1) + AssertEq(len(x), copy(c, x)) + + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +func (t *DeepEqualsTest) OtherSliceValue() { + x := []uint16{17, 19} + m := DeepEquals(x) + ExpectEq("deep equals: [17 19]", m.Description()) + + var c []uint16 + var err error + + // Matching. + c = make([]uint16, len(x)) + AssertEq(len(x), copy(c, x)) + + err = m.Matches(c) + ExpectEq(nil, err) + + // Nil slice. + c = []uint16(nil) + err = m.Matches(c) + ExpectThat(err, Error(Equals("which is nil"))) + + // Prefix. + AssertGt(len(x), 1) + c = make([]uint16, len(x)-1) + AssertEq(len(x)-1, copy(c, x)) + + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) + + // Suffix. + c = make([]uint16, len(x)+1) + AssertEq(len(x), copy(c, x)) + + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +func (t *DeepEqualsTest) NilByteSliceValue() { + x := []byte(nil) + m := DeepEquals(x) + ExpectEq("deep equals: ", m.Description()) + + var c []byte + var err error + + // Nil slice. + c = []byte(nil) + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-nil slice. + c = []byte{} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +func (t *DeepEqualsTest) NilOtherSliceValue() { + x := []uint16(nil) + m := DeepEquals(x) + ExpectEq("deep equals: ", m.Description()) + + var c []uint16 + var err error + + // Nil slice. + c = []uint16(nil) + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-nil slice. + c = []uint16{} + err = m.Matches(c) + ExpectThat(err, Error(Equals(""))) +} + +//////////////////////////////////////////////////////////////////////// +// Benchmarks +//////////////////////////////////////////////////////////////////////// + +func benchmarkWithSize(b *testing.B, size int) { + b.StopTimer() + buf := bytes.Repeat([]byte{0x01}, size) + bufCopy := make([]byte, size) + copy(bufCopy, buf) + + matcher := DeepEquals(buf) + b.StartTimer() + + for i := 0; i < b.N; i++ { + matcher.Matches(bufCopy) + } + + b.SetBytes(int64(size)) +} + +func BenchmarkShortByteSlice(b *testing.B) { + benchmarkWithSize(b, 256) +} + +func BenchmarkLongByteSlice(b *testing.B) { + benchmarkWithSize(b, 1<<24) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are.go new file mode 100644 index 0000000..2941847 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are.go @@ -0,0 +1,91 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +// Given a list of arguments M, ElementsAre returns a matcher that matches +// arrays and slices A where all of the following hold: +// +// * A is the same length as M. +// +// * For each i < len(A) where M[i] is a matcher, A[i] matches M[i]. +// +// * For each i < len(A) where M[i] is not a matcher, A[i] matches +// Equals(M[i]). +// +func ElementsAre(M ...interface{}) Matcher { + // Copy over matchers, or convert to Equals(x) for non-matcher x. + subMatchers := make([]Matcher, len(M)) + for i, x := range M { + if matcher, ok := x.(Matcher); ok { + subMatchers[i] = matcher + continue + } + + subMatchers[i] = Equals(x) + } + + return &elementsAreMatcher{subMatchers} +} + +type elementsAreMatcher struct { + subMatchers []Matcher +} + +func (m *elementsAreMatcher) Description() string { + subDescs := make([]string, len(m.subMatchers)) + for i, sm := range m.subMatchers { + subDescs[i] = sm.Description() + } + + return fmt.Sprintf("elements are: [%s]", strings.Join(subDescs, ", ")) +} + +func (m *elementsAreMatcher) Matches(candidates interface{}) error { + // The candidate must be a slice or an array. + v := reflect.ValueOf(candidates) + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + return NewFatalError("which is not a slice or array") + } + + // The length must be correct. + if v.Len() != len(m.subMatchers) { + return errors.New(fmt.Sprintf("which is of length %d", v.Len())) + } + + // Check each element. + for i, subMatcher := range m.subMatchers { + c := v.Index(i) + if matchErr := subMatcher.Matches(c.Interface()); matchErr != nil { + // Return an errors indicating which element doesn't match. If the + // matcher error was fatal, make this one fatal too. + err := errors.New(fmt.Sprintf("whose element %d doesn't match", i)) + if _, isFatal := matchErr.(*FatalError); isFatal { + err = NewFatalError(err.Error()) + } + + return err + } + } + + return nil +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are_test.go new file mode 100644 index 0000000..a39601b --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/elements_are_test.go @@ -0,0 +1,208 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type ElementsAreTest struct { +} + +func init() { RegisterTestSuite(&ElementsAreTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *ElementsAreTest) EmptySet() { + m := ElementsAre() + ExpectEq("elements are: []", m.Description()) + + var c []interface{} + var err error + + // No candidates. + c = []interface{}{} + err = m.Matches(c) + ExpectEq(nil, err) + + // One candidate. + c = []interface{}{17} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 1"))) +} + +func (t *ElementsAreTest) OneMatcher() { + m := ElementsAre(LessThan(17)) + ExpectEq("elements are: [less than 17]", m.Description()) + + var c []interface{} + var err error + + // No candidates. + c = []interface{}{} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 0"))) + + // Matching candidate. + c = []interface{}{16} + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching candidate. + c = []interface{}{19} + err = m.Matches(c) + ExpectNe(nil, err) + + // Two candidates. + c = []interface{}{17, 19} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 2"))) +} + +func (t *ElementsAreTest) OneValue() { + m := ElementsAre(17) + ExpectEq("elements are: [17]", m.Description()) + + var c []interface{} + var err error + + // No candidates. + c = []interface{}{} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 0"))) + + // Matching int. + c = []interface{}{int(17)} + err = m.Matches(c) + ExpectEq(nil, err) + + // Matching float. + c = []interface{}{float32(17)} + err = m.Matches(c) + ExpectEq(nil, err) + + // Non-matching candidate. + c = []interface{}{19} + err = m.Matches(c) + ExpectNe(nil, err) + + // Two candidates. + c = []interface{}{17, 19} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 2"))) +} + +func (t *ElementsAreTest) MultipleElements() { + m := ElementsAre("taco", LessThan(17)) + ExpectEq("elements are: [taco, less than 17]", m.Description()) + + var c []interface{} + var err error + + // One candidate. + c = []interface{}{17} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 1"))) + + // Both matching. + c = []interface{}{"taco", 16} + err = m.Matches(c) + ExpectEq(nil, err) + + // First non-matching. + c = []interface{}{"burrito", 16} + err = m.Matches(c) + ExpectThat(err, Error(Equals("whose element 0 doesn't match"))) + + // Second non-matching. + c = []interface{}{"taco", 17} + err = m.Matches(c) + ExpectThat(err, Error(Equals("whose element 1 doesn't match"))) + + // Three candidates. + c = []interface{}{"taco", 17, 19} + err = m.Matches(c) + ExpectThat(err, Error(HasSubstr("length 3"))) +} + +func (t *ElementsAreTest) ArrayCandidates() { + m := ElementsAre("taco", LessThan(17)) + + var err error + + // One candidate. + err = m.Matches([1]interface{}{"taco"}) + ExpectThat(err, Error(HasSubstr("length 1"))) + + // Both matching. + err = m.Matches([2]interface{}{"taco", 16}) + ExpectEq(nil, err) + + // First non-matching. + err = m.Matches([2]interface{}{"burrito", 16}) + ExpectThat(err, Error(Equals("whose element 0 doesn't match"))) +} + +func (t *ElementsAreTest) WrongTypeCandidate() { + m := ElementsAre("taco") + + var err error + + // String candidate. + err = m.Matches("taco") + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("array"))) + ExpectThat(err, Error(HasSubstr("slice"))) + + // Map candidate. + err = m.Matches(map[string]string{}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("array"))) + ExpectThat(err, Error(HasSubstr("slice"))) + + // Nil candidate. + err = m.Matches(nil) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("array"))) + ExpectThat(err, Error(HasSubstr("slice"))) +} + +func (t *ElementsAreTest) PropagatesFatality() { + m := ElementsAre(LessThan(17)) + ExpectEq("elements are: [less than 17]", m.Description()) + + var c []interface{} + var err error + + // Non-fatal error. + c = []interface{}{19} + err = m.Matches(c) + AssertNe(nil, err) + ExpectFalse(isFatal(err)) + + // Fatal error. + c = []interface{}{"taco"} + err = m.Matches(c) + AssertNe(nil, err) + ExpectTrue(isFatal(err)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go new file mode 100644 index 0000000..a510707 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go @@ -0,0 +1,541 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "math" + "reflect" +) + +// Equals(x) returns a matcher that matches values v such that v and x are +// equivalent. This includes the case when the comparison v == x using Go's +// built-in comparison operator is legal (except for structs, which this +// matcher does not support), but for convenience the following rules also +// apply: +// +// * Type checking is done based on underlying types rather than actual +// types, so that e.g. two aliases for string can be compared: +// +// type stringAlias1 string +// type stringAlias2 string +// +// a := "taco" +// b := stringAlias1("taco") +// c := stringAlias2("taco") +// +// ExpectTrue(a == b) // Legal, passes +// ExpectTrue(b == c) // Illegal, doesn't compile +// +// ExpectThat(a, Equals(b)) // Passes +// ExpectThat(b, Equals(c)) // Passes +// +// * Values of numeric type are treated as if they were abstract numbers, and +// compared accordingly. Therefore Equals(17) will match int(17), +// int16(17), uint(17), float32(17), complex64(17), and so on. +// +// If you want a stricter matcher that contains no such cleverness, see +// IdenticalTo instead. +// +// Arrays are supported by this matcher, but do not participate in the +// exceptions above. Two arrays compared with this matcher must have identical +// types, and their element type must itself be comparable according to Go's == +// operator. +func Equals(x interface{}) Matcher { + v := reflect.ValueOf(x) + + // This matcher doesn't support structs. + if v.Kind() == reflect.Struct { + panic(fmt.Sprintf("oglematchers.Equals: unsupported kind %v", v.Kind())) + } + + // The == operator is not defined for non-nil slices. + if v.Kind() == reflect.Slice && v.Pointer() != uintptr(0) { + panic(fmt.Sprintf("oglematchers.Equals: non-nil slice")) + } + + return &equalsMatcher{v} +} + +type equalsMatcher struct { + expectedValue reflect.Value +} + +//////////////////////////////////////////////////////////////////////// +// Numeric types +//////////////////////////////////////////////////////////////////////// + +func isSignedInteger(v reflect.Value) bool { + k := v.Kind() + return k >= reflect.Int && k <= reflect.Int64 +} + +func isUnsignedInteger(v reflect.Value) bool { + k := v.Kind() + return k >= reflect.Uint && k <= reflect.Uintptr +} + +func isInteger(v reflect.Value) bool { + return isSignedInteger(v) || isUnsignedInteger(v) +} + +func isFloat(v reflect.Value) bool { + k := v.Kind() + return k == reflect.Float32 || k == reflect.Float64 +} + +func isComplex(v reflect.Value) bool { + k := v.Kind() + return k == reflect.Complex64 || k == reflect.Complex128 +} + +func checkAgainstInt64(e int64, c reflect.Value) (err error) { + err = errors.New("") + + switch { + case isSignedInteger(c): + if c.Int() == e { + err = nil + } + + case isUnsignedInteger(c): + u := c.Uint() + if u <= math.MaxInt64 && int64(u) == e { + err = nil + } + + // Turn around the various floating point types so that the checkAgainst* + // functions for them can deal with precision issues. + case isFloat(c), isComplex(c): + return Equals(c.Interface()).Matches(e) + + default: + err = NewFatalError("which is not numeric") + } + + return +} + +func checkAgainstUint64(e uint64, c reflect.Value) (err error) { + err = errors.New("") + + switch { + case isSignedInteger(c): + i := c.Int() + if i >= 0 && uint64(i) == e { + err = nil + } + + case isUnsignedInteger(c): + if c.Uint() == e { + err = nil + } + + // Turn around the various floating point types so that the checkAgainst* + // functions for them can deal with precision issues. + case isFloat(c), isComplex(c): + return Equals(c.Interface()).Matches(e) + + default: + err = NewFatalError("which is not numeric") + } + + return +} + +func checkAgainstFloat32(e float32, c reflect.Value) (err error) { + err = errors.New("") + + switch { + case isSignedInteger(c): + if float32(c.Int()) == e { + err = nil + } + + case isUnsignedInteger(c): + if float32(c.Uint()) == e { + err = nil + } + + case isFloat(c): + // Compare using float32 to avoid a false sense of precision; otherwise + // e.g. Equals(float32(0.1)) won't match float32(0.1). + if float32(c.Float()) == e { + err = nil + } + + case isComplex(c): + comp := c.Complex() + rl := real(comp) + im := imag(comp) + + // Compare using float32 to avoid a false sense of precision; otherwise + // e.g. Equals(float32(0.1)) won't match (0.1 + 0i). + if im == 0 && float32(rl) == e { + err = nil + } + + default: + err = NewFatalError("which is not numeric") + } + + return +} + +func checkAgainstFloat64(e float64, c reflect.Value) (err error) { + err = errors.New("") + + ck := c.Kind() + + switch { + case isSignedInteger(c): + if float64(c.Int()) == e { + err = nil + } + + case isUnsignedInteger(c): + if float64(c.Uint()) == e { + err = nil + } + + // If the actual value is lower precision, turn the comparison around so we + // apply the low-precision rules. Otherwise, e.g. Equals(0.1) may not match + // float32(0.1). + case ck == reflect.Float32 || ck == reflect.Complex64: + return Equals(c.Interface()).Matches(e) + + // Otherwise, compare with double precision. + case isFloat(c): + if c.Float() == e { + err = nil + } + + case isComplex(c): + comp := c.Complex() + rl := real(comp) + im := imag(comp) + + if im == 0 && rl == e { + err = nil + } + + default: + err = NewFatalError("which is not numeric") + } + + return +} + +func checkAgainstComplex64(e complex64, c reflect.Value) (err error) { + err = errors.New("") + realPart := real(e) + imaginaryPart := imag(e) + + switch { + case isInteger(c) || isFloat(c): + // If we have no imaginary part, then we should just compare against the + // real part. Otherwise, we can't be equal. + if imaginaryPart != 0 { + return + } + + return checkAgainstFloat32(realPart, c) + + case isComplex(c): + // Compare using complex64 to avoid a false sense of precision; otherwise + // e.g. Equals(0.1 + 0i) won't match float32(0.1). + if complex64(c.Complex()) == e { + err = nil + } + + default: + err = NewFatalError("which is not numeric") + } + + return +} + +func checkAgainstComplex128(e complex128, c reflect.Value) (err error) { + err = errors.New("") + realPart := real(e) + imaginaryPart := imag(e) + + switch { + case isInteger(c) || isFloat(c): + // If we have no imaginary part, then we should just compare against the + // real part. Otherwise, we can't be equal. + if imaginaryPart != 0 { + return + } + + return checkAgainstFloat64(realPart, c) + + case isComplex(c): + if c.Complex() == e { + err = nil + } + + default: + err = NewFatalError("which is not numeric") + } + + return +} + +//////////////////////////////////////////////////////////////////////// +// Other types +//////////////////////////////////////////////////////////////////////// + +func checkAgainstBool(e bool, c reflect.Value) (err error) { + if c.Kind() != reflect.Bool { + err = NewFatalError("which is not a bool") + return + } + + err = errors.New("") + if c.Bool() == e { + err = nil + } + return +} + +func checkAgainstChan(e reflect.Value, c reflect.Value) (err error) { + // Create a description of e's type, e.g. "chan int". + typeStr := fmt.Sprintf("%s %s", e.Type().ChanDir(), e.Type().Elem()) + + // Make sure c is a chan of the correct type. + if c.Kind() != reflect.Chan || + c.Type().ChanDir() != e.Type().ChanDir() || + c.Type().Elem() != e.Type().Elem() { + err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr)) + return + } + + err = errors.New("") + if c.Pointer() == e.Pointer() { + err = nil + } + return +} + +func checkAgainstFunc(e reflect.Value, c reflect.Value) (err error) { + // Make sure c is a function. + if c.Kind() != reflect.Func { + err = NewFatalError("which is not a function") + return + } + + err = errors.New("") + if c.Pointer() == e.Pointer() { + err = nil + } + return +} + +func checkAgainstMap(e reflect.Value, c reflect.Value) (err error) { + // Make sure c is a map. + if c.Kind() != reflect.Map { + err = NewFatalError("which is not a map") + return + } + + err = errors.New("") + if c.Pointer() == e.Pointer() { + err = nil + } + return +} + +func checkAgainstPtr(e reflect.Value, c reflect.Value) (err error) { + // Create a description of e's type, e.g. "*int". + typeStr := fmt.Sprintf("*%v", e.Type().Elem()) + + // Make sure c is a pointer of the correct type. + if c.Kind() != reflect.Ptr || + c.Type().Elem() != e.Type().Elem() { + err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr)) + return + } + + err = errors.New("") + if c.Pointer() == e.Pointer() { + err = nil + } + return +} + +func checkAgainstSlice(e reflect.Value, c reflect.Value) (err error) { + // Create a description of e's type, e.g. "[]int". + typeStr := fmt.Sprintf("[]%v", e.Type().Elem()) + + // Make sure c is a slice of the correct type. + if c.Kind() != reflect.Slice || + c.Type().Elem() != e.Type().Elem() { + err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr)) + return + } + + err = errors.New("") + if c.Pointer() == e.Pointer() { + err = nil + } + return +} + +func checkAgainstString(e reflect.Value, c reflect.Value) (err error) { + // Make sure c is a string. + if c.Kind() != reflect.String { + err = NewFatalError("which is not a string") + return + } + + err = errors.New("") + if c.String() == e.String() { + err = nil + } + return +} + +func checkAgainstArray(e reflect.Value, c reflect.Value) (err error) { + // Create a description of e's type, e.g. "[2]int". + typeStr := fmt.Sprintf("%v", e.Type()) + + // Make sure c is the correct type. + if c.Type() != e.Type() { + err = NewFatalError(fmt.Sprintf("which is not %s", typeStr)) + return + } + + // Check for equality. + if e.Interface() != c.Interface() { + err = errors.New("") + return + } + + return +} + +func checkAgainstUnsafePointer(e reflect.Value, c reflect.Value) (err error) { + // Make sure c is a pointer. + if c.Kind() != reflect.UnsafePointer { + err = NewFatalError("which is not a unsafe.Pointer") + return + } + + err = errors.New("") + if c.Pointer() == e.Pointer() { + err = nil + } + return +} + +func checkForNil(c reflect.Value) (err error) { + err = errors.New("") + + // Make sure it is legal to call IsNil. + switch c.Kind() { + case reflect.Invalid: + case reflect.Chan: + case reflect.Func: + case reflect.Interface: + case reflect.Map: + case reflect.Ptr: + case reflect.Slice: + + default: + err = NewFatalError("which cannot be compared to nil") + return + } + + // Ask whether the value is nil. Handle a nil literal (kind Invalid) + // specially, since it's not legal to call IsNil there. + if c.Kind() == reflect.Invalid || c.IsNil() { + err = nil + } + return +} + +//////////////////////////////////////////////////////////////////////// +// Public implementation +//////////////////////////////////////////////////////////////////////// + +func (m *equalsMatcher) Matches(candidate interface{}) error { + e := m.expectedValue + c := reflect.ValueOf(candidate) + ek := e.Kind() + + switch { + case ek == reflect.Bool: + return checkAgainstBool(e.Bool(), c) + + case isSignedInteger(e): + return checkAgainstInt64(e.Int(), c) + + case isUnsignedInteger(e): + return checkAgainstUint64(e.Uint(), c) + + case ek == reflect.Float32: + return checkAgainstFloat32(float32(e.Float()), c) + + case ek == reflect.Float64: + return checkAgainstFloat64(e.Float(), c) + + case ek == reflect.Complex64: + return checkAgainstComplex64(complex64(e.Complex()), c) + + case ek == reflect.Complex128: + return checkAgainstComplex128(complex128(e.Complex()), c) + + case ek == reflect.Chan: + return checkAgainstChan(e, c) + + case ek == reflect.Func: + return checkAgainstFunc(e, c) + + case ek == reflect.Map: + return checkAgainstMap(e, c) + + case ek == reflect.Ptr: + return checkAgainstPtr(e, c) + + case ek == reflect.Slice: + return checkAgainstSlice(e, c) + + case ek == reflect.String: + return checkAgainstString(e, c) + + case ek == reflect.Array: + return checkAgainstArray(e, c) + + case ek == reflect.UnsafePointer: + return checkAgainstUnsafePointer(e, c) + + case ek == reflect.Invalid: + return checkForNil(c) + } + + panic(fmt.Sprintf("equalsMatcher.Matches: unexpected kind: %v", ek)) +} + +func (m *equalsMatcher) Description() string { + // Special case: handle nil. + if !m.expectedValue.IsValid() { + return "is nil" + } + + return fmt.Sprintf("%v", m.expectedValue.Interface()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals_test.go new file mode 100644 index 0000000..8135127 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals_test.go @@ -0,0 +1,3864 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "fmt" + "math" + "unsafe" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +var someInt int = -17 + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type EqualsTest struct { +} + +func init() { RegisterTestSuite(&EqualsTest{}) } + +type equalsTestCase struct { + candidate interface{} + expectedResult bool + shouldBeFatal bool + expectedError string +} + +func (t *EqualsTest) checkTestCases(matcher Matcher, cases []equalsTestCase) { + for i, c := range cases { + err := matcher.Matches(c.candidate) + ExpectEq( + c.expectedResult, + (err == nil), + "Result for case %d: %v (Error: %v)", i, c, err) + + if err == nil { + continue + } + + _, isFatal := err.(*FatalError) + ExpectEq(c.shouldBeFatal, isFatal, "Fatality for case %d: %v", i, c) + + ExpectThat(err, Error(Equals(c.expectedError)), "Case %d: %v", i, c) + } +} + +//////////////////////////////////////////////////////////////////////// +// nil +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) EqualsNil() { + matcher := Equals(nil) + ExpectEq("is nil", matcher.Description()) + + cases := []equalsTestCase{ + // Legal types + equalsTestCase{nil, true, false, ""}, + equalsTestCase{chan int(nil), true, false, ""}, + equalsTestCase{(func())(nil), true, false, ""}, + equalsTestCase{interface{}(nil), true, false, ""}, + equalsTestCase{map[int]int(nil), true, false, ""}, + equalsTestCase{(*int)(nil), true, false, ""}, + equalsTestCase{[]int(nil), true, false, ""}, + + equalsTestCase{make(chan int), false, false, ""}, + equalsTestCase{func() {}, false, false, ""}, + equalsTestCase{map[int]int{}, false, false, ""}, + equalsTestCase{&someInt, false, false, ""}, + equalsTestCase{[]int{}, false, false, ""}, + + // Illegal types + equalsTestCase{17, false, true, "which cannot be compared to nil"}, + equalsTestCase{int8(17), false, true, "which cannot be compared to nil"}, + equalsTestCase{uintptr(17), false, true, "which cannot be compared to nil"}, + equalsTestCase{[...]int{}, false, true, "which cannot be compared to nil"}, + equalsTestCase{"taco", false, true, "which cannot be compared to nil"}, + equalsTestCase{equalsTestCase{}, false, true, "which cannot be compared to nil"}, + equalsTestCase{unsafe.Pointer(&someInt), false, true, "which cannot be compared to nil"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeIntegerLiteral() { + // -2^30 + matcher := Equals(-1073741824) + ExpectEq("-1073741824", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -1073741824. + equalsTestCase{-1073741824, true, false, ""}, + equalsTestCase{-1073741824.0, true, false, ""}, + equalsTestCase{-1073741824 + 0i, true, false, ""}, + equalsTestCase{int(-1073741824), true, false, ""}, + equalsTestCase{int32(-1073741824), true, false, ""}, + equalsTestCase{int64(-1073741824), true, false, ""}, + equalsTestCase{float32(-1073741824), true, false, ""}, + equalsTestCase{float64(-1073741824), true, false, ""}, + equalsTestCase{complex64(-1073741824), true, false, ""}, + equalsTestCase{complex128(-1073741824), true, false, ""}, + equalsTestCase{interface{}(int(-1073741824)), true, false, ""}, + + // Values that would be -1073741824 in two's complement. + equalsTestCase{uint((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 1073741824), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 1073741824), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int(-1073741823), false, false, ""}, + equalsTestCase{int32(-1073741823), false, false, ""}, + equalsTestCase{int64(-1073741823), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float64(-1073741824.1), false, false, ""}, + equalsTestCase{float64(-1073741823.9), false, false, ""}, + equalsTestCase{complex128(-1073741823), false, false, ""}, + equalsTestCase{complex128(-1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveIntegerLiteral() { + // 2^30 + matcher := Equals(1073741824) + ExpectEq("1073741824", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 1073741824. + equalsTestCase{1073741824, true, false, ""}, + equalsTestCase{1073741824.0, true, false, ""}, + equalsTestCase{1073741824 + 0i, true, false, ""}, + equalsTestCase{int(1073741824), true, false, ""}, + equalsTestCase{uint(1073741824), true, false, ""}, + equalsTestCase{int32(1073741824), true, false, ""}, + equalsTestCase{int64(1073741824), true, false, ""}, + equalsTestCase{uint32(1073741824), true, false, ""}, + equalsTestCase{uint64(1073741824), true, false, ""}, + equalsTestCase{uintptr(1073741824), true, false, ""}, + equalsTestCase{float32(1073741824), true, false, ""}, + equalsTestCase{float64(1073741824), true, false, ""}, + equalsTestCase{complex64(1073741824), true, false, ""}, + equalsTestCase{complex128(1073741824), true, false, ""}, + equalsTestCase{interface{}(int(1073741824)), true, false, ""}, + equalsTestCase{interface{}(uint(1073741824)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(1073741823), false, false, ""}, + equalsTestCase{int32(1073741823), false, false, ""}, + equalsTestCase{int64(1073741823), false, false, ""}, + equalsTestCase{float64(1073741824.1), false, false, ""}, + equalsTestCase{float64(1073741823.9), false, false, ""}, + equalsTestCase{complex128(1073741823), false, false, ""}, + equalsTestCase{complex128(1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Floating point literals +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeIntegralFloatingPointLiteral() { + // -2^30 + matcher := Equals(-1073741824.0) + ExpectEq("-1.073741824e+09", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -1073741824. + equalsTestCase{-1073741824, true, false, ""}, + equalsTestCase{-1073741824.0, true, false, ""}, + equalsTestCase{-1073741824 + 0i, true, false, ""}, + equalsTestCase{int(-1073741824), true, false, ""}, + equalsTestCase{int32(-1073741824), true, false, ""}, + equalsTestCase{int64(-1073741824), true, false, ""}, + equalsTestCase{float32(-1073741824), true, false, ""}, + equalsTestCase{float64(-1073741824), true, false, ""}, + equalsTestCase{complex64(-1073741824), true, false, ""}, + equalsTestCase{complex128(-1073741824), true, false, ""}, + equalsTestCase{interface{}(int(-1073741824)), true, false, ""}, + equalsTestCase{interface{}(float64(-1073741824)), true, false, ""}, + + // Values that would be -1073741824 in two's complement. + equalsTestCase{uint((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 1073741824), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 1073741824), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int(-1073741823), false, false, ""}, + equalsTestCase{int32(-1073741823), false, false, ""}, + equalsTestCase{int64(-1073741823), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float64(-1073741824.1), false, false, ""}, + equalsTestCase{float64(-1073741823.9), false, false, ""}, + equalsTestCase{complex128(-1073741823), false, false, ""}, + equalsTestCase{complex128(-1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveIntegralFloatingPointLiteral() { + // 2^30 + matcher := Equals(1073741824.0) + ExpectEq("1.073741824e+09", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 1073741824. + equalsTestCase{1073741824, true, false, ""}, + equalsTestCase{1073741824.0, true, false, ""}, + equalsTestCase{1073741824 + 0i, true, false, ""}, + equalsTestCase{int(1073741824), true, false, ""}, + equalsTestCase{int32(1073741824), true, false, ""}, + equalsTestCase{int64(1073741824), true, false, ""}, + equalsTestCase{uint(1073741824), true, false, ""}, + equalsTestCase{uint32(1073741824), true, false, ""}, + equalsTestCase{uint64(1073741824), true, false, ""}, + equalsTestCase{float32(1073741824), true, false, ""}, + equalsTestCase{float64(1073741824), true, false, ""}, + equalsTestCase{complex64(1073741824), true, false, ""}, + equalsTestCase{complex128(1073741824), true, false, ""}, + equalsTestCase{interface{}(int(1073741824)), true, false, ""}, + equalsTestCase{interface{}(float64(1073741824)), true, false, ""}, + + // Values that would be 1073741824 in two's complement. + equalsTestCase{uint((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 1073741824), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 1073741824), false, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(1073741823), false, false, ""}, + equalsTestCase{int32(1073741823), false, false, ""}, + equalsTestCase{int64(1073741823), false, false, ""}, + equalsTestCase{uint(1073741823), false, false, ""}, + equalsTestCase{uint32(1073741823), false, false, ""}, + equalsTestCase{uint64(1073741823), false, false, ""}, + equalsTestCase{float64(1073741824.1), false, false, ""}, + equalsTestCase{float64(1073741823.9), false, false, ""}, + equalsTestCase{complex128(1073741823), false, false, ""}, + equalsTestCase{complex128(1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NonIntegralFloatingPointLiteral() { + matcher := Equals(17.1) + ExpectEq("17.1", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 17.1. + equalsTestCase{17.1, true, false, ""}, + equalsTestCase{17.1, true, false, ""}, + equalsTestCase{17.1 + 0i, true, false, ""}, + equalsTestCase{float32(17.1), true, false, ""}, + equalsTestCase{float64(17.1), true, false, ""}, + equalsTestCase{complex64(17.1), true, false, ""}, + equalsTestCase{complex128(17.1), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{17, false, false, ""}, + equalsTestCase{17.2, false, false, ""}, + equalsTestCase{18, false, false, ""}, + equalsTestCase{int(17), false, false, ""}, + equalsTestCase{int(18), false, false, ""}, + equalsTestCase{int32(17), false, false, ""}, + equalsTestCase{int64(17), false, false, ""}, + equalsTestCase{uint(17), false, false, ""}, + equalsTestCase{uint32(17), false, false, ""}, + equalsTestCase{uint64(17), false, false, ""}, + equalsTestCase{uintptr(17), false, false, ""}, + equalsTestCase{complex128(17.1 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// bool +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) False() { + matcher := Equals(false) + ExpectEq("false", matcher.Description()) + + cases := []equalsTestCase{ + // bools + equalsTestCase{false, true, false, ""}, + equalsTestCase{bool(false), true, false, ""}, + + equalsTestCase{true, false, false, ""}, + equalsTestCase{bool(true), false, false, ""}, + + // Other types. + equalsTestCase{int(0), false, true, "which is not a bool"}, + equalsTestCase{int8(0), false, true, "which is not a bool"}, + equalsTestCase{int16(0), false, true, "which is not a bool"}, + equalsTestCase{int32(0), false, true, "which is not a bool"}, + equalsTestCase{int64(0), false, true, "which is not a bool"}, + equalsTestCase{uint(0), false, true, "which is not a bool"}, + equalsTestCase{uint8(0), false, true, "which is not a bool"}, + equalsTestCase{uint16(0), false, true, "which is not a bool"}, + equalsTestCase{uint32(0), false, true, "which is not a bool"}, + equalsTestCase{uint64(0), false, true, "which is not a bool"}, + equalsTestCase{uintptr(0), false, true, "which is not a bool"}, + equalsTestCase{[...]int{}, false, true, "which is not a bool"}, + equalsTestCase{make(chan int), false, true, "which is not a bool"}, + equalsTestCase{func() {}, false, true, "which is not a bool"}, + equalsTestCase{map[int]int{}, false, true, "which is not a bool"}, + equalsTestCase{&someInt, false, true, "which is not a bool"}, + equalsTestCase{[]int{}, false, true, "which is not a bool"}, + equalsTestCase{"taco", false, true, "which is not a bool"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a bool"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) True() { + matcher := Equals(true) + ExpectEq("true", matcher.Description()) + + cases := []equalsTestCase{ + // bools + equalsTestCase{true, true, false, ""}, + equalsTestCase{bool(true), true, false, ""}, + + equalsTestCase{false, false, false, ""}, + equalsTestCase{bool(false), false, false, ""}, + + // Other types. + equalsTestCase{int(1), false, true, "which is not a bool"}, + equalsTestCase{int8(1), false, true, "which is not a bool"}, + equalsTestCase{int16(1), false, true, "which is not a bool"}, + equalsTestCase{int32(1), false, true, "which is not a bool"}, + equalsTestCase{int64(1), false, true, "which is not a bool"}, + equalsTestCase{uint(1), false, true, "which is not a bool"}, + equalsTestCase{uint8(1), false, true, "which is not a bool"}, + equalsTestCase{uint16(1), false, true, "which is not a bool"}, + equalsTestCase{uint32(1), false, true, "which is not a bool"}, + equalsTestCase{uint64(1), false, true, "which is not a bool"}, + equalsTestCase{uintptr(1), false, true, "which is not a bool"}, + equalsTestCase{[...]int{}, false, true, "which is not a bool"}, + equalsTestCase{make(chan int), false, true, "which is not a bool"}, + equalsTestCase{func() {}, false, true, "which is not a bool"}, + equalsTestCase{map[int]int{}, false, true, "which is not a bool"}, + equalsTestCase{&someInt, false, true, "which is not a bool"}, + equalsTestCase{[]int{}, false, true, "which is not a bool"}, + equalsTestCase{"taco", false, true, "which is not a bool"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a bool"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// int +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeInt() { + // -2^30 + matcher := Equals(int(-1073741824)) + ExpectEq("-1073741824", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -1073741824. + equalsTestCase{-1073741824, true, false, ""}, + equalsTestCase{-1073741824.0, true, false, ""}, + equalsTestCase{-1073741824 + 0i, true, false, ""}, + equalsTestCase{int(-1073741824), true, false, ""}, + equalsTestCase{int32(-1073741824), true, false, ""}, + equalsTestCase{int64(-1073741824), true, false, ""}, + equalsTestCase{float32(-1073741824), true, false, ""}, + equalsTestCase{float64(-1073741824), true, false, ""}, + equalsTestCase{complex64(-1073741824), true, false, ""}, + equalsTestCase{complex128(-1073741824), true, false, ""}, + equalsTestCase{interface{}(int(-1073741824)), true, false, ""}, + + // Values that would be -1073741824 in two's complement. + equalsTestCase{uint((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 1073741824), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 1073741824), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int(-1073741823), false, false, ""}, + equalsTestCase{int32(-1073741823), false, false, ""}, + equalsTestCase{int64(-1073741823), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float64(-1073741824.1), false, false, ""}, + equalsTestCase{float64(-1073741823.9), false, false, ""}, + equalsTestCase{complex128(-1073741823), false, false, ""}, + equalsTestCase{complex128(-1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveInt() { + // 2^30 + matcher := Equals(int(1073741824)) + ExpectEq("1073741824", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 1073741824. + equalsTestCase{1073741824, true, false, ""}, + equalsTestCase{1073741824.0, true, false, ""}, + equalsTestCase{1073741824 + 0i, true, false, ""}, + equalsTestCase{int(1073741824), true, false, ""}, + equalsTestCase{uint(1073741824), true, false, ""}, + equalsTestCase{int32(1073741824), true, false, ""}, + equalsTestCase{int64(1073741824), true, false, ""}, + equalsTestCase{uint32(1073741824), true, false, ""}, + equalsTestCase{uint64(1073741824), true, false, ""}, + equalsTestCase{uintptr(1073741824), true, false, ""}, + equalsTestCase{float32(1073741824), true, false, ""}, + equalsTestCase{float64(1073741824), true, false, ""}, + equalsTestCase{complex64(1073741824), true, false, ""}, + equalsTestCase{complex128(1073741824), true, false, ""}, + equalsTestCase{interface{}(int(1073741824)), true, false, ""}, + equalsTestCase{interface{}(uint(1073741824)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(1073741823), false, false, ""}, + equalsTestCase{int32(1073741823), false, false, ""}, + equalsTestCase{int64(1073741823), false, false, ""}, + equalsTestCase{float64(1073741824.1), false, false, ""}, + equalsTestCase{float64(1073741823.9), false, false, ""}, + equalsTestCase{complex128(1073741823), false, false, ""}, + equalsTestCase{complex128(1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// int8 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeInt8() { + matcher := Equals(int8(-17)) + ExpectEq("-17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -17. + equalsTestCase{-17, true, false, ""}, + equalsTestCase{-17.0, true, false, ""}, + equalsTestCase{-17 + 0i, true, false, ""}, + equalsTestCase{int(-17), true, false, ""}, + equalsTestCase{int8(-17), true, false, ""}, + equalsTestCase{int16(-17), true, false, ""}, + equalsTestCase{int32(-17), true, false, ""}, + equalsTestCase{int64(-17), true, false, ""}, + equalsTestCase{float32(-17), true, false, ""}, + equalsTestCase{float64(-17), true, false, ""}, + equalsTestCase{complex64(-17), true, false, ""}, + equalsTestCase{complex128(-17), true, false, ""}, + equalsTestCase{interface{}(int(-17)), true, false, ""}, + + // Values that would be -17 in two's complement. + equalsTestCase{uint((1 << 32) - 17), false, false, ""}, + equalsTestCase{uint8((1 << 8) - 17), false, false, ""}, + equalsTestCase{uint16((1 << 16) - 17), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 17), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 17), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 17), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int(-16), false, false, ""}, + equalsTestCase{int8(-16), false, false, ""}, + equalsTestCase{int16(-16), false, false, ""}, + equalsTestCase{int32(-16), false, false, ""}, + equalsTestCase{int64(-16), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float32(-17.1), false, false, ""}, + equalsTestCase{float32(-16.9), false, false, ""}, + equalsTestCase{complex64(-16), false, false, ""}, + equalsTestCase{complex64(-17 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{-17}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{-17}, false, true, "which is not numeric"}, + equalsTestCase{"-17", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ZeroInt8() { + matcher := Equals(int8(0)) + ExpectEq("0", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 0. + equalsTestCase{0, true, false, ""}, + equalsTestCase{0.0, true, false, ""}, + equalsTestCase{0 + 0i, true, false, ""}, + equalsTestCase{int(0), true, false, ""}, + equalsTestCase{int8(0), true, false, ""}, + equalsTestCase{int16(0), true, false, ""}, + equalsTestCase{int32(0), true, false, ""}, + equalsTestCase{int64(0), true, false, ""}, + equalsTestCase{float32(0), true, false, ""}, + equalsTestCase{float64(0), true, false, ""}, + equalsTestCase{complex64(0), true, false, ""}, + equalsTestCase{complex128(0), true, false, ""}, + equalsTestCase{interface{}(int(0)), true, false, ""}, + equalsTestCase{uint(0), true, false, ""}, + equalsTestCase{uint8(0), true, false, ""}, + equalsTestCase{uint16(0), true, false, ""}, + equalsTestCase{uint32(0), true, false, ""}, + equalsTestCase{uint64(0), true, false, ""}, + equalsTestCase{uintptr(0), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(1), false, false, ""}, + equalsTestCase{int8(1), false, false, ""}, + equalsTestCase{int16(1), false, false, ""}, + equalsTestCase{int32(1), false, false, ""}, + equalsTestCase{int64(1), false, false, ""}, + equalsTestCase{float32(-0.1), false, false, ""}, + equalsTestCase{float32(0.1), false, false, ""}, + equalsTestCase{complex64(1), false, false, ""}, + equalsTestCase{complex64(0 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{0}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{0}, false, true, "which is not numeric"}, + equalsTestCase{"0", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveInt8() { + matcher := Equals(int8(17)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 17. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(17), true, false, ""}, + equalsTestCase{int8(17), true, false, ""}, + equalsTestCase{int16(17), true, false, ""}, + equalsTestCase{int32(17), true, false, ""}, + equalsTestCase{int64(17), true, false, ""}, + equalsTestCase{float32(17), true, false, ""}, + equalsTestCase{float64(17), true, false, ""}, + equalsTestCase{complex64(17), true, false, ""}, + equalsTestCase{complex128(17), true, false, ""}, + equalsTestCase{interface{}(int(17)), true, false, ""}, + equalsTestCase{uint(17), true, false, ""}, + equalsTestCase{uint8(17), true, false, ""}, + equalsTestCase{uint16(17), true, false, ""}, + equalsTestCase{uint32(17), true, false, ""}, + equalsTestCase{uint64(17), true, false, ""}, + equalsTestCase{uintptr(17), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(16), false, false, ""}, + equalsTestCase{int8(16), false, false, ""}, + equalsTestCase{int16(16), false, false, ""}, + equalsTestCase{int32(16), false, false, ""}, + equalsTestCase{int64(16), false, false, ""}, + equalsTestCase{float32(16.9), false, false, ""}, + equalsTestCase{float32(17.1), false, false, ""}, + equalsTestCase{complex64(16), false, false, ""}, + equalsTestCase{complex64(17 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{17}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{17}, false, true, "which is not numeric"}, + equalsTestCase{"17", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// int16 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeInt16() { + matcher := Equals(int16(-32766)) + ExpectEq("-32766", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -32766. + equalsTestCase{-32766, true, false, ""}, + equalsTestCase{-32766.0, true, false, ""}, + equalsTestCase{-32766 + 0i, true, false, ""}, + equalsTestCase{int(-32766), true, false, ""}, + equalsTestCase{int16(-32766), true, false, ""}, + equalsTestCase{int32(-32766), true, false, ""}, + equalsTestCase{int64(-32766), true, false, ""}, + equalsTestCase{float32(-32766), true, false, ""}, + equalsTestCase{float64(-32766), true, false, ""}, + equalsTestCase{complex64(-32766), true, false, ""}, + equalsTestCase{complex128(-32766), true, false, ""}, + equalsTestCase{interface{}(int(-32766)), true, false, ""}, + + // Values that would be -32766 in two's complement. + equalsTestCase{uint((1 << 32) - 32766), false, false, ""}, + equalsTestCase{uint16((1 << 16) - 32766), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 32766), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 32766), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 32766), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int(-16), false, false, ""}, + equalsTestCase{int8(-16), false, false, ""}, + equalsTestCase{int16(-16), false, false, ""}, + equalsTestCase{int32(-16), false, false, ""}, + equalsTestCase{int64(-16), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float32(-32766.1), false, false, ""}, + equalsTestCase{float32(-32765.9), false, false, ""}, + equalsTestCase{complex64(-32766.1), false, false, ""}, + equalsTestCase{complex64(-32766 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{-32766}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{-32766}, false, true, "which is not numeric"}, + equalsTestCase{"-32766", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ZeroInt16() { + matcher := Equals(int16(0)) + ExpectEq("0", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 0. + equalsTestCase{0, true, false, ""}, + equalsTestCase{0.0, true, false, ""}, + equalsTestCase{0 + 0i, true, false, ""}, + equalsTestCase{int(0), true, false, ""}, + equalsTestCase{int8(0), true, false, ""}, + equalsTestCase{int16(0), true, false, ""}, + equalsTestCase{int32(0), true, false, ""}, + equalsTestCase{int64(0), true, false, ""}, + equalsTestCase{float32(0), true, false, ""}, + equalsTestCase{float64(0), true, false, ""}, + equalsTestCase{complex64(0), true, false, ""}, + equalsTestCase{complex128(0), true, false, ""}, + equalsTestCase{interface{}(int(0)), true, false, ""}, + equalsTestCase{uint(0), true, false, ""}, + equalsTestCase{uint8(0), true, false, ""}, + equalsTestCase{uint16(0), true, false, ""}, + equalsTestCase{uint32(0), true, false, ""}, + equalsTestCase{uint64(0), true, false, ""}, + equalsTestCase{uintptr(0), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(1), false, false, ""}, + equalsTestCase{int8(1), false, false, ""}, + equalsTestCase{int16(1), false, false, ""}, + equalsTestCase{int32(1), false, false, ""}, + equalsTestCase{int64(1), false, false, ""}, + equalsTestCase{float32(-0.1), false, false, ""}, + equalsTestCase{float32(0.1), false, false, ""}, + equalsTestCase{complex64(1), false, false, ""}, + equalsTestCase{complex64(0 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{0}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{0}, false, true, "which is not numeric"}, + equalsTestCase{"0", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveInt16() { + matcher := Equals(int16(32765)) + ExpectEq("32765", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 32765. + equalsTestCase{32765, true, false, ""}, + equalsTestCase{32765.0, true, false, ""}, + equalsTestCase{32765 + 0i, true, false, ""}, + equalsTestCase{int(32765), true, false, ""}, + equalsTestCase{int16(32765), true, false, ""}, + equalsTestCase{int32(32765), true, false, ""}, + equalsTestCase{int64(32765), true, false, ""}, + equalsTestCase{float32(32765), true, false, ""}, + equalsTestCase{float64(32765), true, false, ""}, + equalsTestCase{complex64(32765), true, false, ""}, + equalsTestCase{complex128(32765), true, false, ""}, + equalsTestCase{interface{}(int(32765)), true, false, ""}, + equalsTestCase{uint(32765), true, false, ""}, + equalsTestCase{uint16(32765), true, false, ""}, + equalsTestCase{uint32(32765), true, false, ""}, + equalsTestCase{uint64(32765), true, false, ""}, + equalsTestCase{uintptr(32765), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(32764), false, false, ""}, + equalsTestCase{int16(32764), false, false, ""}, + equalsTestCase{int32(32764), false, false, ""}, + equalsTestCase{int64(32764), false, false, ""}, + equalsTestCase{float32(32764.9), false, false, ""}, + equalsTestCase{float32(32765.1), false, false, ""}, + equalsTestCase{complex64(32765.9), false, false, ""}, + equalsTestCase{complex64(32765 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{32765}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{32765}, false, true, "which is not numeric"}, + equalsTestCase{"32765", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// int32 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeInt32() { + // -2^30 + matcher := Equals(int32(-1073741824)) + ExpectEq("-1073741824", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -1073741824. + equalsTestCase{-1073741824, true, false, ""}, + equalsTestCase{-1073741824.0, true, false, ""}, + equalsTestCase{-1073741824 + 0i, true, false, ""}, + equalsTestCase{int(-1073741824), true, false, ""}, + equalsTestCase{int32(-1073741824), true, false, ""}, + equalsTestCase{int64(-1073741824), true, false, ""}, + equalsTestCase{float32(-1073741824), true, false, ""}, + equalsTestCase{float64(-1073741824), true, false, ""}, + equalsTestCase{complex64(-1073741824), true, false, ""}, + equalsTestCase{complex128(-1073741824), true, false, ""}, + equalsTestCase{interface{}(int(-1073741824)), true, false, ""}, + + // Values that would be -1073741824 in two's complement. + equalsTestCase{uint((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint32((1 << 32) - 1073741824), false, false, ""}, + equalsTestCase{uint64((1 << 64) - 1073741824), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 1073741824), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int(-1073741823), false, false, ""}, + equalsTestCase{int32(-1073741823), false, false, ""}, + equalsTestCase{int64(-1073741823), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float64(-1073741824.1), false, false, ""}, + equalsTestCase{float64(-1073741823.9), false, false, ""}, + equalsTestCase{complex128(-1073741823), false, false, ""}, + equalsTestCase{complex128(-1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveInt32() { + // 2^30 + matcher := Equals(int32(1073741824)) + ExpectEq("1073741824", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 1073741824. + equalsTestCase{1073741824, true, false, ""}, + equalsTestCase{1073741824.0, true, false, ""}, + equalsTestCase{1073741824 + 0i, true, false, ""}, + equalsTestCase{int(1073741824), true, false, ""}, + equalsTestCase{uint(1073741824), true, false, ""}, + equalsTestCase{int32(1073741824), true, false, ""}, + equalsTestCase{int64(1073741824), true, false, ""}, + equalsTestCase{uint32(1073741824), true, false, ""}, + equalsTestCase{uint64(1073741824), true, false, ""}, + equalsTestCase{uintptr(1073741824), true, false, ""}, + equalsTestCase{float32(1073741824), true, false, ""}, + equalsTestCase{float64(1073741824), true, false, ""}, + equalsTestCase{complex64(1073741824), true, false, ""}, + equalsTestCase{complex128(1073741824), true, false, ""}, + equalsTestCase{interface{}(int(1073741824)), true, false, ""}, + equalsTestCase{interface{}(uint(1073741824)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(1073741823), false, false, ""}, + equalsTestCase{int32(1073741823), false, false, ""}, + equalsTestCase{int64(1073741823), false, false, ""}, + equalsTestCase{float64(1073741824.1), false, false, ""}, + equalsTestCase{float64(1073741823.9), false, false, ""}, + equalsTestCase{complex128(1073741823), false, false, ""}, + equalsTestCase{complex128(1073741824 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// int64 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeInt64() { + // -2^40 + matcher := Equals(int64(-1099511627776)) + ExpectEq("-1099511627776", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -1099511627776. + equalsTestCase{-1099511627776.0, true, false, ""}, + equalsTestCase{-1099511627776 + 0i, true, false, ""}, + equalsTestCase{int64(-1099511627776), true, false, ""}, + equalsTestCase{float32(-1099511627776), true, false, ""}, + equalsTestCase{float64(-1099511627776), true, false, ""}, + equalsTestCase{complex64(-1099511627776), true, false, ""}, + equalsTestCase{complex128(-1099511627776), true, false, ""}, + equalsTestCase{interface{}(int64(-1099511627776)), true, false, ""}, + + // Values that would be -1099511627776 in two's complement. + equalsTestCase{uint64((1 << 64) - 1099511627776), false, false, ""}, + + // Non-equal values of signed integer type. + equalsTestCase{int64(-1099511627775), false, false, ""}, + + // Non-equal values of other numeric types. + equalsTestCase{float64(-1099511627776.1), false, false, ""}, + equalsTestCase{float64(-1099511627775.9), false, false, ""}, + equalsTestCase{complex128(-1099511627775), false, false, ""}, + equalsTestCase{complex128(-1099511627776 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveInt64() { + // 2^40 + matcher := Equals(int64(1099511627776)) + ExpectEq("1099511627776", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 1099511627776. + equalsTestCase{1099511627776.0, true, false, ""}, + equalsTestCase{1099511627776 + 0i, true, false, ""}, + equalsTestCase{int64(1099511627776), true, false, ""}, + equalsTestCase{uint64(1099511627776), true, false, ""}, + equalsTestCase{uintptr(1099511627776), true, false, ""}, + equalsTestCase{float32(1099511627776), true, false, ""}, + equalsTestCase{float64(1099511627776), true, false, ""}, + equalsTestCase{complex64(1099511627776), true, false, ""}, + equalsTestCase{complex128(1099511627776), true, false, ""}, + equalsTestCase{interface{}(int64(1099511627776)), true, false, ""}, + equalsTestCase{interface{}(uint64(1099511627776)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(1099511627775), false, false, ""}, + equalsTestCase{uint64(1099511627775), false, false, ""}, + equalsTestCase{float64(1099511627776.1), false, false, ""}, + equalsTestCase{float64(1099511627775.9), false, false, ""}, + equalsTestCase{complex128(1099511627775), false, false, ""}, + equalsTestCase{complex128(1099511627776 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Int64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := Equals(int64(kTwoTo25 + 1)) + ExpectEq("33554433", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{int64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Single-precision floating point. + equalsTestCase{float32(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float32(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{float64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{complex128(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 2), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Int64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := Equals(int64(kTwoTo54 + 1)) + ExpectEq("18014398509481985", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo54 + 0), false, false, ""}, + equalsTestCase{int64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 2), false, false, ""}, + + equalsTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{float64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 3), false, false, ""}, + + equalsTestCase{complex128(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{complex128(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// uint +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) SmallUint() { + const kExpected = 17 + matcher := Equals(uint(kExpected)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int8(kExpected), true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint8(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{kExpected + 1, false, false, ""}, + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int8(kExpected + 1), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeUint() { + const kExpected = (1 << 16) + 17 + matcher := Equals(uint(kExpected)) + ExpectEq("65553", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{65553, true, false, ""}, + equalsTestCase{65553.0, true, false, ""}, + equalsTestCase{65553 + 0i, true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{int16(17), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(17), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) UintNotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := Equals(uint(kTwoTo25 + 1)) + ExpectEq("33554433", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{int64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Single-precision floating point. + equalsTestCase{float32(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float32(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{float64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{complex128(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 2), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// uint8 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) SmallUint8() { + const kExpected = 17 + matcher := Equals(uint8(kExpected)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int8(kExpected), true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint8(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{kExpected + 1, false, false, ""}, + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int8(kExpected + 1), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// uint16 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) SmallUint16() { + const kExpected = 17 + matcher := Equals(uint16(kExpected)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int8(kExpected), true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint8(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{kExpected + 1, false, false, ""}, + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int8(kExpected + 1), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeUint16() { + const kExpected = (1 << 8) + 17 + matcher := Equals(uint16(kExpected)) + ExpectEq("273", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{273, true, false, ""}, + equalsTestCase{273.0, true, false, ""}, + equalsTestCase{273 + 0i, true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{int8(17), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(17), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// uint32 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) SmallUint32() { + const kExpected = 17 + matcher := Equals(uint32(kExpected)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int8(kExpected), true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint8(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{kExpected + 1, false, false, ""}, + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int8(kExpected + 1), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeUint32() { + const kExpected = (1 << 16) + 17 + matcher := Equals(uint32(kExpected)) + ExpectEq("65553", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{65553, true, false, ""}, + equalsTestCase{65553.0, true, false, ""}, + equalsTestCase{65553 + 0i, true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{int16(17), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(17), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Uint32NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := Equals(uint32(kTwoTo25 + 1)) + ExpectEq("33554433", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{int64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Single-precision floating point. + equalsTestCase{float32(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float32(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{float64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{complex128(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 2), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// uint64 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) SmallUint64() { + const kExpected = 17 + matcher := Equals(uint64(kExpected)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int8(kExpected), true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint8(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{kExpected + 1, false, false, ""}, + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int8(kExpected + 1), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeUint64() { + const kExpected = (1 << 32) + 17 + matcher := Equals(uint64(kExpected)) + ExpectEq("4294967313", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{4294967313.0, true, false, ""}, + equalsTestCase{4294967313 + 0i, true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{int(17), false, false, ""}, + equalsTestCase{int32(17), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(17), false, false, ""}, + equalsTestCase{uint32(17), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Uint64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := Equals(uint64(kTwoTo25 + 1)) + ExpectEq("33554433", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{int64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Single-precision floating point. + equalsTestCase{float32(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float32(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{float64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 2), false, false, ""}, + + equalsTestCase{complex128(kTwoTo25 + 0), false, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 2), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Uint64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := Equals(uint64(kTwoTo54 + 1)) + ExpectEq("18014398509481985", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo54 + 0), false, false, ""}, + equalsTestCase{int64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 2), false, false, ""}, + + equalsTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{float64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 3), false, false, ""}, + + equalsTestCase{complex128(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{complex128(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// uintptr +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) SmallUintptr() { + const kExpected = 17 + matcher := Equals(uintptr(kExpected)) + ExpectEq("17", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{17, true, false, ""}, + equalsTestCase{17.0, true, false, ""}, + equalsTestCase{17 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int8(kExpected), true, false, ""}, + equalsTestCase{int16(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint8(kExpected), true, false, ""}, + equalsTestCase{uint16(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{kExpected + 1, false, false, ""}, + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int8(kExpected + 1), false, false, ""}, + equalsTestCase{int16(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint8(kExpected + 1), false, false, ""}, + equalsTestCase{uint16(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeUintptr() { + const kExpected = (1 << 32) + 17 + matcher := Equals(uintptr(kExpected)) + ExpectEq("4294967313", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{4294967313.0, true, false, ""}, + equalsTestCase{4294967313 + 0i, true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric types. + equalsTestCase{int(17), false, false, ""}, + equalsTestCase{int32(17), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(17), false, false, ""}, + equalsTestCase{uint32(17), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected + 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 1), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// float32 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeIntegralFloat32() { + matcher := Equals(float32(-32769)) + ExpectEq("-32769", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -32769. + equalsTestCase{-32769.0, true, false, ""}, + equalsTestCase{-32769 + 0i, true, false, ""}, + equalsTestCase{int32(-32769), true, false, ""}, + equalsTestCase{int64(-32769), true, false, ""}, + equalsTestCase{float32(-32769), true, false, ""}, + equalsTestCase{float64(-32769), true, false, ""}, + equalsTestCase{complex64(-32769), true, false, ""}, + equalsTestCase{complex128(-32769), true, false, ""}, + equalsTestCase{interface{}(float32(-32769)), true, false, ""}, + equalsTestCase{interface{}(int64(-32769)), true, false, ""}, + + // Values that would be -32769 in two's complement. + equalsTestCase{uint64((1 << 64) - 32769), false, false, ""}, + equalsTestCase{uintptr((1 << 64) - 32769), false, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(-32770), false, false, ""}, + equalsTestCase{float32(-32769.1), false, false, ""}, + equalsTestCase{float32(-32768.9), false, false, ""}, + equalsTestCase{float64(-32769.1), false, false, ""}, + equalsTestCase{float64(-32768.9), false, false, ""}, + equalsTestCase{complex128(-32768), false, false, ""}, + equalsTestCase{complex128(-32769 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NegativeNonIntegralFloat32() { + matcher := Equals(float32(-32769.1)) + ExpectEq("-32769.1", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of -32769.1. + equalsTestCase{-32769.1, true, false, ""}, + equalsTestCase{-32769.1 + 0i, true, false, ""}, + equalsTestCase{float32(-32769.1), true, false, ""}, + equalsTestCase{float64(-32769.1), true, false, ""}, + equalsTestCase{complex64(-32769.1), true, false, ""}, + equalsTestCase{complex128(-32769.1), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int32(-32769), false, false, ""}, + equalsTestCase{int32(-32770), false, false, ""}, + equalsTestCase{int64(-32769), false, false, ""}, + equalsTestCase{int64(-32770), false, false, ""}, + equalsTestCase{float32(-32769.2), false, false, ""}, + equalsTestCase{float32(-32769.0), false, false, ""}, + equalsTestCase{float64(-32769.2), false, false, ""}, + equalsTestCase{complex128(-32769.1 + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeNegativeFloat32() { + const kExpected = -1 * (1 << 65) + matcher := Equals(float32(kExpected)) + ExpectEq("-3.689349e+19", matcher.Description()) + + floatExpected := float32(kExpected) + castedInt := int64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ZeroFloat32() { + matcher := Equals(float32(0)) + ExpectEq("0", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of zero. + equalsTestCase{0.0, true, false, ""}, + equalsTestCase{0 + 0i, true, false, ""}, + equalsTestCase{int(0), true, false, ""}, + equalsTestCase{int8(0), true, false, ""}, + equalsTestCase{int16(0), true, false, ""}, + equalsTestCase{int32(0), true, false, ""}, + equalsTestCase{int64(0), true, false, ""}, + equalsTestCase{uint(0), true, false, ""}, + equalsTestCase{uint8(0), true, false, ""}, + equalsTestCase{uint16(0), true, false, ""}, + equalsTestCase{uint32(0), true, false, ""}, + equalsTestCase{uint64(0), true, false, ""}, + equalsTestCase{uintptr(0), true, false, ""}, + equalsTestCase{float32(0), true, false, ""}, + equalsTestCase{float64(0), true, false, ""}, + equalsTestCase{complex64(0), true, false, ""}, + equalsTestCase{complex128(0), true, false, ""}, + equalsTestCase{interface{}(float32(0)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(1), false, false, ""}, + equalsTestCase{int64(-1), false, false, ""}, + equalsTestCase{float32(1), false, false, ""}, + equalsTestCase{float32(-1), false, false, ""}, + equalsTestCase{complex128(0 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveIntegralFloat32() { + matcher := Equals(float32(32769)) + ExpectEq("32769", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 32769. + equalsTestCase{32769.0, true, false, ""}, + equalsTestCase{32769 + 0i, true, false, ""}, + equalsTestCase{int(32769), true, false, ""}, + equalsTestCase{int32(32769), true, false, ""}, + equalsTestCase{int64(32769), true, false, ""}, + equalsTestCase{uint(32769), true, false, ""}, + equalsTestCase{uint32(32769), true, false, ""}, + equalsTestCase{uint64(32769), true, false, ""}, + equalsTestCase{uintptr(32769), true, false, ""}, + equalsTestCase{float32(32769), true, false, ""}, + equalsTestCase{float64(32769), true, false, ""}, + equalsTestCase{complex64(32769), true, false, ""}, + equalsTestCase{complex128(32769), true, false, ""}, + equalsTestCase{interface{}(float32(32769)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(32770), false, false, ""}, + equalsTestCase{uint64(32770), false, false, ""}, + equalsTestCase{float32(32769.1), false, false, ""}, + equalsTestCase{float32(32768.9), false, false, ""}, + equalsTestCase{float64(32769.1), false, false, ""}, + equalsTestCase{float64(32768.9), false, false, ""}, + equalsTestCase{complex128(32768), false, false, ""}, + equalsTestCase{complex128(32769 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveNonIntegralFloat32() { + matcher := Equals(float32(32769.1)) + ExpectEq("32769.1", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 32769.1. + equalsTestCase{32769.1, true, false, ""}, + equalsTestCase{32769.1 + 0i, true, false, ""}, + equalsTestCase{float32(32769.1), true, false, ""}, + equalsTestCase{float64(32769.1), true, false, ""}, + equalsTestCase{complex64(32769.1), true, false, ""}, + equalsTestCase{complex128(32769.1), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int32(32769), false, false, ""}, + equalsTestCase{int32(32770), false, false, ""}, + equalsTestCase{uint64(32769), false, false, ""}, + equalsTestCase{uint64(32770), false, false, ""}, + equalsTestCase{float32(32769.2), false, false, ""}, + equalsTestCase{float32(32769.0), false, false, ""}, + equalsTestCase{float64(32769.2), false, false, ""}, + equalsTestCase{complex128(32769.1 + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargePositiveFloat32() { + const kExpected = 1 << 65 + matcher := Equals(float32(kExpected)) + ExpectEq("3.689349e+19", matcher.Description()) + + floatExpected := float32(kExpected) + castedInt := uint64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{uint64(0), false, false, ""}, + equalsTestCase{uint64(math.MaxUint64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Float32AboveExactIntegerRange() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := Equals(float32(kTwoTo25 + 1)) + ExpectEq("3.3554432e+07", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{int64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{uint64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{uint64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 3), false, false, ""}, + + // Single-precision floating point. + equalsTestCase{float32(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float32(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex128(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex128(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// float64 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeIntegralFloat64() { + const kExpected = -(1 << 50) + matcher := Equals(float64(kExpected)) + ExpectEq("-1.125899906842624e+15", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{-1125899906842624.0, true, false, ""}, + equalsTestCase{-1125899906842624.0 + 0i, true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + equalsTestCase{interface{}(float64(kExpected)), true, false, ""}, + + // Values that would be kExpected in two's complement. + equalsTestCase{uint64((1 << 64) + kExpected), false, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float32(kExpected + (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.5), false, false, ""}, + equalsTestCase{float64(kExpected + 0.5), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NegativeNonIntegralFloat64() { + const kTwoTo50 = 1 << 50 + const kExpected = -kTwoTo50 - 0.25 + + matcher := Equals(float64(kExpected)) + ExpectEq("-1.1258999068426242e+15", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(-kTwoTo50), false, false, ""}, + equalsTestCase{int64(-kTwoTo50 - 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.25), false, false, ""}, + equalsTestCase{float64(kExpected + 0.25), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeNegativeFloat64() { + const kExpected = -1 * (1 << 65) + matcher := Equals(float64(kExpected)) + ExpectEq("-3.6893488147419103e+19", matcher.Description()) + + floatExpected := float64(kExpected) + castedInt := int64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ZeroFloat64() { + matcher := Equals(float64(0)) + ExpectEq("0", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of zero. + equalsTestCase{0.0, true, false, ""}, + equalsTestCase{0 + 0i, true, false, ""}, + equalsTestCase{int(0), true, false, ""}, + equalsTestCase{int8(0), true, false, ""}, + equalsTestCase{int16(0), true, false, ""}, + equalsTestCase{int32(0), true, false, ""}, + equalsTestCase{int64(0), true, false, ""}, + equalsTestCase{uint(0), true, false, ""}, + equalsTestCase{uint8(0), true, false, ""}, + equalsTestCase{uint16(0), true, false, ""}, + equalsTestCase{uint32(0), true, false, ""}, + equalsTestCase{uint64(0), true, false, ""}, + equalsTestCase{uintptr(0), true, false, ""}, + equalsTestCase{float32(0), true, false, ""}, + equalsTestCase{float64(0), true, false, ""}, + equalsTestCase{complex64(0), true, false, ""}, + equalsTestCase{complex128(0), true, false, ""}, + equalsTestCase{interface{}(float32(0)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(1), false, false, ""}, + equalsTestCase{int64(-1), false, false, ""}, + equalsTestCase{float32(1), false, false, ""}, + equalsTestCase{float32(-1), false, false, ""}, + equalsTestCase{complex128(0 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveIntegralFloat64() { + const kExpected = 1 << 50 + matcher := Equals(float64(kExpected)) + ExpectEq("1.125899906842624e+15", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 32769. + equalsTestCase{1125899906842624.0, true, false, ""}, + equalsTestCase{1125899906842624.0 + 0i, true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + equalsTestCase{interface{}(float64(kExpected)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float32(kExpected + (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.5), false, false, ""}, + equalsTestCase{float64(kExpected + 0.5), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveNonIntegralFloat64() { + const kTwoTo50 = 1 << 50 + const kExpected = kTwoTo50 + 0.25 + matcher := Equals(float64(kExpected)) + ExpectEq("1.1258999068426242e+15", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kTwoTo50), false, false, ""}, + equalsTestCase{int64(kTwoTo50 - 1), false, false, ""}, + equalsTestCase{float64(kExpected - 0.25), false, false, ""}, + equalsTestCase{float64(kExpected + 0.25), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargePositiveFloat64() { + const kExpected = 1 << 65 + matcher := Equals(float64(kExpected)) + ExpectEq("3.6893488147419103e+19", matcher.Description()) + + floatExpected := float64(kExpected) + castedInt := uint64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{uint64(0), false, false, ""}, + equalsTestCase{uint64(math.MaxUint64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Float64AboveExactIntegerRange() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := Equals(float64(kTwoTo54 + 1)) + ExpectEq("1.8014398509481984e+16", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{int64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 3), false, false, ""}, + + equalsTestCase{uint64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{float64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 3), false, false, ""}, + + equalsTestCase{complex128(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{complex128(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// complex64 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeIntegralComplex64() { + const kExpected = -32769 + matcher := Equals(complex64(kExpected)) + ExpectEq("(-32769+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{-32769.0, true, false, ""}, + equalsTestCase{-32769.0 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + equalsTestCase{interface{}(float64(kExpected)), true, false, ""}, + + // Values that would be kExpected in two's complement. + equalsTestCase{uint32((1 << 32) + kExpected), false, false, ""}, + equalsTestCase{uint64((1 << 64) + kExpected), false, false, ""}, + equalsTestCase{uintptr((1 << 64) + kExpected), false, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float32(kExpected + (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.5), false, false, ""}, + equalsTestCase{float64(kExpected + 0.5), false, false, ""}, + equalsTestCase{complex64(kExpected - 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NegativeNonIntegralComplex64() { + const kTwoTo20 = 1 << 20 + const kExpected = -kTwoTo20 - 0.25 + + matcher := Equals(complex64(kExpected)) + ExpectEq("(-1.0485762e+06+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(-kTwoTo20), false, false, ""}, + equalsTestCase{int(-kTwoTo20 - 1), false, false, ""}, + equalsTestCase{int32(-kTwoTo20), false, false, ""}, + equalsTestCase{int32(-kTwoTo20 - 1), false, false, ""}, + equalsTestCase{int64(-kTwoTo20), false, false, ""}, + equalsTestCase{int64(-kTwoTo20 - 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.25), false, false, ""}, + equalsTestCase{float64(kExpected + 0.25), false, false, ""}, + equalsTestCase{complex64(kExpected - 0.75), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected - 0.75), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeNegativeComplex64() { + const kExpected = -1 * (1 << 65) + matcher := Equals(complex64(kExpected)) + ExpectEq("(-3.689349e+19+0i)", matcher.Description()) + + floatExpected := float64(kExpected) + castedInt := int64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ZeroComplex64() { + matcher := Equals(complex64(0)) + ExpectEq("(0+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of zero. + equalsTestCase{0.0, true, false, ""}, + equalsTestCase{0 + 0i, true, false, ""}, + equalsTestCase{int(0), true, false, ""}, + equalsTestCase{int8(0), true, false, ""}, + equalsTestCase{int16(0), true, false, ""}, + equalsTestCase{int32(0), true, false, ""}, + equalsTestCase{int64(0), true, false, ""}, + equalsTestCase{uint(0), true, false, ""}, + equalsTestCase{uint8(0), true, false, ""}, + equalsTestCase{uint16(0), true, false, ""}, + equalsTestCase{uint32(0), true, false, ""}, + equalsTestCase{uint64(0), true, false, ""}, + equalsTestCase{uintptr(0), true, false, ""}, + equalsTestCase{float32(0), true, false, ""}, + equalsTestCase{float64(0), true, false, ""}, + equalsTestCase{complex64(0), true, false, ""}, + equalsTestCase{complex128(0), true, false, ""}, + equalsTestCase{interface{}(float32(0)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(1), false, false, ""}, + equalsTestCase{int64(-1), false, false, ""}, + equalsTestCase{float32(1), false, false, ""}, + equalsTestCase{float32(-1), false, false, ""}, + equalsTestCase{float64(1), false, false, ""}, + equalsTestCase{float64(-1), false, false, ""}, + equalsTestCase{complex64(0 + 2i), false, false, ""}, + equalsTestCase{complex128(0 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveIntegralComplex64() { + const kExpected = 1 << 20 + matcher := Equals(complex64(kExpected)) + ExpectEq("(1.048576e+06+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 32769. + equalsTestCase{1048576.0, true, false, ""}, + equalsTestCase{1048576.0 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + equalsTestCase{interface{}(float64(kExpected)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float32(kExpected + (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.5), false, false, ""}, + equalsTestCase{float64(kExpected + 0.5), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveNonIntegralComplex64() { + const kTwoTo20 = 1 << 20 + const kExpected = kTwoTo20 + 0.25 + matcher := Equals(complex64(kExpected)) + ExpectEq("(1.0485762e+06+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kTwoTo20), false, false, ""}, + equalsTestCase{int64(kTwoTo20 - 1), false, false, ""}, + equalsTestCase{uint64(kTwoTo20), false, false, ""}, + equalsTestCase{uint64(kTwoTo20 - 1), false, false, ""}, + equalsTestCase{float32(kExpected - 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected - 0.25), false, false, ""}, + equalsTestCase{float64(kExpected + 0.25), false, false, ""}, + equalsTestCase{complex64(kExpected - 1), false, false, ""}, + equalsTestCase{complex64(kExpected - 1i), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected - 1i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargePositiveComplex64() { + const kExpected = 1 << 65 + matcher := Equals(complex64(kExpected)) + ExpectEq("(3.689349e+19+0i)", matcher.Description()) + + floatExpected := float64(kExpected) + castedInt := uint64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{uint64(0), false, false, ""}, + equalsTestCase{uint64(math.MaxUint64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Complex64AboveExactIntegerRange() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := Equals(complex64(kTwoTo25 + 1)) + ExpectEq("(3.3554432e+07+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{int64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{int64(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{uint64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{uint64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{uint64(kTwoTo25 + 3), false, false, ""}, + + // Single-precision floating point. + equalsTestCase{float32(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float32(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex64(kTwoTo25 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{float64(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{float64(kTwoTo25 + 3), false, false, ""}, + + equalsTestCase{complex128(kTwoTo25 - 2), false, false, ""}, + equalsTestCase{complex128(kTwoTo25 - 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 0), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 2), true, false, ""}, + equalsTestCase{complex128(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Complex64WithNonZeroImaginaryPart() { + const kRealPart = 17 + const kImagPart = 0.25i + const kExpected = kRealPart + kImagPart + matcher := Equals(complex64(kExpected)) + ExpectEq("(17+0.25i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kRealPart + kImagPart, true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(kRealPart), false, false, ""}, + equalsTestCase{int8(kRealPart), false, false, ""}, + equalsTestCase{int16(kRealPart), false, false, ""}, + equalsTestCase{int32(kRealPart), false, false, ""}, + equalsTestCase{int64(kRealPart), false, false, ""}, + equalsTestCase{uint(kRealPart), false, false, ""}, + equalsTestCase{uint8(kRealPart), false, false, ""}, + equalsTestCase{uint16(kRealPart), false, false, ""}, + equalsTestCase{uint32(kRealPart), false, false, ""}, + equalsTestCase{uint64(kRealPart), false, false, ""}, + equalsTestCase{float32(kRealPart), false, false, ""}, + equalsTestCase{float64(kRealPart), false, false, ""}, + equalsTestCase{complex64(kRealPart), false, false, ""}, + equalsTestCase{complex64(kRealPart + kImagPart + 0.5), false, false, ""}, + equalsTestCase{complex64(kRealPart + kImagPart + 0.5i), false, false, ""}, + equalsTestCase{complex128(kRealPart), false, false, ""}, + equalsTestCase{complex128(kRealPart + kImagPart + 0.5), false, false, ""}, + equalsTestCase{complex128(kRealPart + kImagPart + 0.5i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// complex128 +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NegativeIntegralComplex128() { + const kExpected = -32769 + matcher := Equals(complex128(kExpected)) + ExpectEq("(-32769+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{-32769.0, true, false, ""}, + equalsTestCase{-32769.0 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + equalsTestCase{interface{}(float64(kExpected)), true, false, ""}, + + // Values that would be kExpected in two's complement. + equalsTestCase{uint32((1 << 32) + kExpected), false, false, ""}, + equalsTestCase{uint64((1 << 64) + kExpected), false, false, ""}, + equalsTestCase{uintptr((1 << 64) + kExpected), false, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float32(kExpected + (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.5), false, false, ""}, + equalsTestCase{float64(kExpected + 0.5), false, false, ""}, + equalsTestCase{complex64(kExpected - 1), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NegativeNonIntegralComplex128() { + const kTwoTo20 = 1 << 20 + const kExpected = -kTwoTo20 - 0.25 + + matcher := Equals(complex128(kExpected)) + ExpectEq("(-1.04857625e+06+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(-kTwoTo20), false, false, ""}, + equalsTestCase{int(-kTwoTo20 - 1), false, false, ""}, + equalsTestCase{int32(-kTwoTo20), false, false, ""}, + equalsTestCase{int32(-kTwoTo20 - 1), false, false, ""}, + equalsTestCase{int64(-kTwoTo20), false, false, ""}, + equalsTestCase{int64(-kTwoTo20 - 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.25), false, false, ""}, + equalsTestCase{float64(kExpected + 0.25), false, false, ""}, + equalsTestCase{complex64(kExpected - 0.75), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected - 0.75), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargeNegativeComplex128() { + const kExpected = -1 * (1 << 65) + matcher := Equals(complex128(kExpected)) + ExpectEq("(-3.6893488147419103e+19+0i)", matcher.Description()) + + floatExpected := float64(kExpected) + castedInt := int64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex64(kExpected + 2i), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ZeroComplex128() { + matcher := Equals(complex128(0)) + ExpectEq("(0+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of zero. + equalsTestCase{0.0, true, false, ""}, + equalsTestCase{0 + 0i, true, false, ""}, + equalsTestCase{int(0), true, false, ""}, + equalsTestCase{int8(0), true, false, ""}, + equalsTestCase{int16(0), true, false, ""}, + equalsTestCase{int32(0), true, false, ""}, + equalsTestCase{int64(0), true, false, ""}, + equalsTestCase{uint(0), true, false, ""}, + equalsTestCase{uint8(0), true, false, ""}, + equalsTestCase{uint16(0), true, false, ""}, + equalsTestCase{uint32(0), true, false, ""}, + equalsTestCase{uint64(0), true, false, ""}, + equalsTestCase{uintptr(0), true, false, ""}, + equalsTestCase{float32(0), true, false, ""}, + equalsTestCase{float64(0), true, false, ""}, + equalsTestCase{complex64(0), true, false, ""}, + equalsTestCase{complex128(0), true, false, ""}, + equalsTestCase{interface{}(float32(0)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(1), false, false, ""}, + equalsTestCase{int64(-1), false, false, ""}, + equalsTestCase{float32(1), false, false, ""}, + equalsTestCase{float32(-1), false, false, ""}, + equalsTestCase{float64(1), false, false, ""}, + equalsTestCase{float64(-1), false, false, ""}, + equalsTestCase{complex64(0 + 2i), false, false, ""}, + equalsTestCase{complex128(0 + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveIntegralComplex128() { + const kExpected = 1 << 20 + matcher := Equals(complex128(kExpected)) + ExpectEq("(1.048576e+06+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of 32769. + equalsTestCase{1048576.0, true, false, ""}, + equalsTestCase{1048576.0 + 0i, true, false, ""}, + equalsTestCase{int(kExpected), true, false, ""}, + equalsTestCase{int32(kExpected), true, false, ""}, + equalsTestCase{int64(kExpected), true, false, ""}, + equalsTestCase{uint(kExpected), true, false, ""}, + equalsTestCase{uint32(kExpected), true, false, ""}, + equalsTestCase{uint64(kExpected), true, false, ""}, + equalsTestCase{uintptr(kExpected), true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + equalsTestCase{interface{}(float64(kExpected)), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(kExpected + 1), false, false, ""}, + equalsTestCase{int32(kExpected + 1), false, false, ""}, + equalsTestCase{int64(kExpected + 1), false, false, ""}, + equalsTestCase{uint(kExpected + 1), false, false, ""}, + equalsTestCase{uint32(kExpected + 1), false, false, ""}, + equalsTestCase{uint64(kExpected + 1), false, false, ""}, + equalsTestCase{uintptr(kExpected + 1), false, false, ""}, + equalsTestCase{float32(kExpected - (1 << 30)), false, false, ""}, + equalsTestCase{float32(kExpected + (1 << 30)), false, false, ""}, + equalsTestCase{float64(kExpected - 0.5), false, false, ""}, + equalsTestCase{float64(kExpected + 0.5), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + + // Non-numeric types. + equalsTestCase{true, false, true, "which is not numeric"}, + equalsTestCase{[...]int{}, false, true, "which is not numeric"}, + equalsTestCase{make(chan int), false, true, "which is not numeric"}, + equalsTestCase{func() {}, false, true, "which is not numeric"}, + equalsTestCase{map[int]int{}, false, true, "which is not numeric"}, + equalsTestCase{&someInt, false, true, "which is not numeric"}, + equalsTestCase{[]int{}, false, true, "which is not numeric"}, + equalsTestCase{"taco", false, true, "which is not numeric"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not numeric"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) PositiveNonIntegralComplex128() { + const kTwoTo20 = 1 << 20 + const kExpected = kTwoTo20 + 0.25 + matcher := Equals(complex128(kExpected)) + ExpectEq("(1.04857625e+06+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int64(kTwoTo20), false, false, ""}, + equalsTestCase{int64(kTwoTo20 - 1), false, false, ""}, + equalsTestCase{uint64(kTwoTo20), false, false, ""}, + equalsTestCase{uint64(kTwoTo20 - 1), false, false, ""}, + equalsTestCase{float32(kExpected - 1), false, false, ""}, + equalsTestCase{float32(kExpected + 1), false, false, ""}, + equalsTestCase{float64(kExpected - 0.25), false, false, ""}, + equalsTestCase{float64(kExpected + 0.25), false, false, ""}, + equalsTestCase{complex64(kExpected - 1), false, false, ""}, + equalsTestCase{complex64(kExpected - 1i), false, false, ""}, + equalsTestCase{complex128(kExpected - 1), false, false, ""}, + equalsTestCase{complex128(kExpected - 1i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) LargePositiveComplex128() { + const kExpected = 1 << 65 + matcher := Equals(complex128(kExpected)) + ExpectEq("(3.6893488147419103e+19+0i)", matcher.Description()) + + floatExpected := float64(kExpected) + castedInt := uint64(floatExpected) + + cases := []equalsTestCase{ + // Equal values of numeric type. + equalsTestCase{kExpected + 0i, true, false, ""}, + equalsTestCase{float32(kExpected), true, false, ""}, + equalsTestCase{float64(kExpected), true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{castedInt, false, false, ""}, + equalsTestCase{int64(0), false, false, ""}, + equalsTestCase{int64(math.MinInt64), false, false, ""}, + equalsTestCase{int64(math.MaxInt64), false, false, ""}, + equalsTestCase{uint64(0), false, false, ""}, + equalsTestCase{uint64(math.MaxUint64), false, false, ""}, + equalsTestCase{float32(kExpected / 2), false, false, ""}, + equalsTestCase{float64(kExpected / 2), false, false, ""}, + equalsTestCase{complex128(kExpected + 2i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Complex128AboveExactIntegerRange() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := Equals(complex128(kTwoTo54 + 1)) + ExpectEq("(1.8014398509481984e+16+0i)", matcher.Description()) + + cases := []equalsTestCase{ + // Integers. + equalsTestCase{int64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{int64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{int64(kTwoTo54 + 3), false, false, ""}, + + equalsTestCase{uint64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{uint64(kTwoTo54 + 3), false, false, ""}, + + // Double-precision floating point. + equalsTestCase{float64(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{float64(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{float64(kTwoTo54 + 3), false, false, ""}, + + equalsTestCase{complex128(kTwoTo54 - 2), false, false, ""}, + equalsTestCase{complex128(kTwoTo54 - 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 0), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 1), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 2), true, false, ""}, + equalsTestCase{complex128(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) Complex128WithNonZeroImaginaryPart() { + const kRealPart = 17 + const kImagPart = 0.25i + const kExpected = kRealPart + kImagPart + matcher := Equals(complex128(kExpected)) + ExpectEq("(17+0.25i)", matcher.Description()) + + cases := []equalsTestCase{ + // Various types of the expected value. + equalsTestCase{kExpected, true, false, ""}, + equalsTestCase{kRealPart + kImagPart, true, false, ""}, + equalsTestCase{complex64(kExpected), true, false, ""}, + equalsTestCase{complex128(kExpected), true, false, ""}, + + // Non-equal values of numeric type. + equalsTestCase{int(kRealPart), false, false, ""}, + equalsTestCase{int8(kRealPart), false, false, ""}, + equalsTestCase{int16(kRealPart), false, false, ""}, + equalsTestCase{int32(kRealPart), false, false, ""}, + equalsTestCase{int64(kRealPart), false, false, ""}, + equalsTestCase{uint(kRealPart), false, false, ""}, + equalsTestCase{uint8(kRealPart), false, false, ""}, + equalsTestCase{uint16(kRealPart), false, false, ""}, + equalsTestCase{uint32(kRealPart), false, false, ""}, + equalsTestCase{uint64(kRealPart), false, false, ""}, + equalsTestCase{float32(kRealPart), false, false, ""}, + equalsTestCase{float64(kRealPart), false, false, ""}, + equalsTestCase{complex64(kRealPart), false, false, ""}, + equalsTestCase{complex64(kRealPart + kImagPart + 0.5), false, false, ""}, + equalsTestCase{complex64(kRealPart + kImagPart + 0.5i), false, false, ""}, + equalsTestCase{complex128(kRealPart), false, false, ""}, + equalsTestCase{complex128(kRealPart + kImagPart + 0.5), false, false, ""}, + equalsTestCase{complex128(kRealPart + kImagPart + 0.5i), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Arrays +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) ArrayOfComparableType() { + expected := [3]uint{17, 19, 23} + + matcher := Equals(expected) + ExpectEq("[17 19 23]", matcher.Description()) + + // To defeat constant de-duping by the compiler. + makeArray := func(i, j, k uint) [3]uint { return [3]uint{i, j, k} } + + type arrayAlias [3]uint + type uintAlias uint + + cases := []equalsTestCase{ + // Correct types, equal. + equalsTestCase{expected, true, false, ""}, + equalsTestCase{[3]uint{17, 19, 23}, true, false, ""}, + equalsTestCase{makeArray(17, 19, 23), true, false, ""}, + + // Correct types, not equal. + equalsTestCase{[3]uint{0, 0, 0}, false, false, ""}, + equalsTestCase{[3]uint{18, 19, 23}, false, false, ""}, + equalsTestCase{[3]uint{17, 20, 23}, false, false, ""}, + equalsTestCase{[3]uint{17, 19, 22}, false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not [3]uint"}, + equalsTestCase{bool(false), false, true, "which is not [3]uint"}, + equalsTestCase{int(0), false, true, "which is not [3]uint"}, + equalsTestCase{int8(0), false, true, "which is not [3]uint"}, + equalsTestCase{int16(0), false, true, "which is not [3]uint"}, + equalsTestCase{int32(0), false, true, "which is not [3]uint"}, + equalsTestCase{int64(0), false, true, "which is not [3]uint"}, + equalsTestCase{uint(0), false, true, "which is not [3]uint"}, + equalsTestCase{uint8(0), false, true, "which is not [3]uint"}, + equalsTestCase{uint16(0), false, true, "which is not [3]uint"}, + equalsTestCase{uint32(0), false, true, "which is not [3]uint"}, + equalsTestCase{uint64(0), false, true, "which is not [3]uint"}, + equalsTestCase{true, false, true, "which is not [3]uint"}, + equalsTestCase{[...]int{}, false, true, "which is not [3]uint"}, + equalsTestCase{func() {}, false, true, "which is not [3]uint"}, + equalsTestCase{map[int]int{}, false, true, "which is not [3]uint"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not [3]uint"}, + equalsTestCase{[2]uint{17, 19}, false, true, "which is not [3]uint"}, + equalsTestCase{[4]uint{17, 19, 23, 0}, false, true, "which is not [3]uint"}, + equalsTestCase{arrayAlias{17, 19, 23}, false, true, "which is not [3]uint"}, + equalsTestCase{[3]uintAlias{17, 19, 23}, false, true, "which is not [3]uint"}, + equalsTestCase{[3]int32{17, 19, 23}, false, true, "which is not [3]uint"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ArrayOfNonComparableType() { + type nonComparableArray [2]map[string]string + f := func() { + ExpectEq(nonComparableArray{}, nonComparableArray{}) + } + + ExpectThat(f, Panics(MatchesRegexp("uncomparable.*nonComparableArray"))) +} + +//////////////////////////////////////////////////////////////////////// +// chan +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NilChan() { + var nilChan1 chan int + var nilChan2 chan int + var nilChan3 chan uint + var nonNilChan1 chan int = make(chan int) + var nonNilChan2 chan uint = make(chan uint) + + matcher := Equals(nilChan1) + ExpectEq("", matcher.Description()) + + cases := []equalsTestCase{ + // int channels + equalsTestCase{nilChan1, true, false, ""}, + equalsTestCase{nilChan2, true, false, ""}, + equalsTestCase{nonNilChan1, false, false, ""}, + + // uint channels + equalsTestCase{nilChan3, false, true, "which is not a chan int"}, + equalsTestCase{nonNilChan2, false, true, "which is not a chan int"}, + + // Other types. + equalsTestCase{0, false, true, "which is not a chan int"}, + equalsTestCase{bool(false), false, true, "which is not a chan int"}, + equalsTestCase{int(0), false, true, "which is not a chan int"}, + equalsTestCase{int8(0), false, true, "which is not a chan int"}, + equalsTestCase{int16(0), false, true, "which is not a chan int"}, + equalsTestCase{int32(0), false, true, "which is not a chan int"}, + equalsTestCase{int64(0), false, true, "which is not a chan int"}, + equalsTestCase{uint(0), false, true, "which is not a chan int"}, + equalsTestCase{uint8(0), false, true, "which is not a chan int"}, + equalsTestCase{uint16(0), false, true, "which is not a chan int"}, + equalsTestCase{uint32(0), false, true, "which is not a chan int"}, + equalsTestCase{uint64(0), false, true, "which is not a chan int"}, + equalsTestCase{true, false, true, "which is not a chan int"}, + equalsTestCase{[...]int{}, false, true, "which is not a chan int"}, + equalsTestCase{func() {}, false, true, "which is not a chan int"}, + equalsTestCase{map[int]int{}, false, true, "which is not a chan int"}, + equalsTestCase{&someInt, false, true, "which is not a chan int"}, + equalsTestCase{[]int{}, false, true, "which is not a chan int"}, + equalsTestCase{"taco", false, true, "which is not a chan int"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a chan int"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NonNilChan() { + var nilChan1 chan int + var nilChan2 chan uint + var nonNilChan1 chan int = make(chan int) + var nonNilChan2 chan int = make(chan int) + var nonNilChan3 chan uint = make(chan uint) + + matcher := Equals(nonNilChan1) + ExpectEq(fmt.Sprintf("%v", nonNilChan1), matcher.Description()) + + cases := []equalsTestCase{ + // int channels + equalsTestCase{nonNilChan1, true, false, ""}, + equalsTestCase{nonNilChan2, false, false, ""}, + equalsTestCase{nilChan1, false, false, ""}, + + // uint channels + equalsTestCase{nilChan2, false, true, "which is not a chan int"}, + equalsTestCase{nonNilChan3, false, true, "which is not a chan int"}, + + // Other types. + equalsTestCase{0, false, true, "which is not a chan int"}, + equalsTestCase{bool(false), false, true, "which is not a chan int"}, + equalsTestCase{int(0), false, true, "which is not a chan int"}, + equalsTestCase{int8(0), false, true, "which is not a chan int"}, + equalsTestCase{int16(0), false, true, "which is not a chan int"}, + equalsTestCase{int32(0), false, true, "which is not a chan int"}, + equalsTestCase{int64(0), false, true, "which is not a chan int"}, + equalsTestCase{uint(0), false, true, "which is not a chan int"}, + equalsTestCase{uint8(0), false, true, "which is not a chan int"}, + equalsTestCase{uint16(0), false, true, "which is not a chan int"}, + equalsTestCase{uint32(0), false, true, "which is not a chan int"}, + equalsTestCase{uint64(0), false, true, "which is not a chan int"}, + equalsTestCase{true, false, true, "which is not a chan int"}, + equalsTestCase{[...]int{}, false, true, "which is not a chan int"}, + equalsTestCase{func() {}, false, true, "which is not a chan int"}, + equalsTestCase{map[int]int{}, false, true, "which is not a chan int"}, + equalsTestCase{&someInt, false, true, "which is not a chan int"}, + equalsTestCase{[]int{}, false, true, "which is not a chan int"}, + equalsTestCase{"taco", false, true, "which is not a chan int"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a chan int"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) ChanDirection() { + var chan1 chan<- int + var chan2 <-chan int + var chan3 chan int + + matcher := Equals(chan1) + ExpectEq(fmt.Sprintf("%v", chan1), matcher.Description()) + + cases := []equalsTestCase{ + equalsTestCase{chan1, true, false, ""}, + equalsTestCase{chan2, false, true, "which is not a chan<- int"}, + equalsTestCase{chan3, false, true, "which is not a chan<- int"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// func +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) Functions() { + func1 := func() {} + func2 := func() {} + func3 := func(x int) {} + + matcher := Equals(func1) + ExpectEq(fmt.Sprintf("%v", func1), matcher.Description()) + + cases := []equalsTestCase{ + // Functions. + equalsTestCase{func1, true, false, ""}, + equalsTestCase{func2, false, false, ""}, + equalsTestCase{func3, false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a function"}, + equalsTestCase{bool(false), false, true, "which is not a function"}, + equalsTestCase{int(0), false, true, "which is not a function"}, + equalsTestCase{int8(0), false, true, "which is not a function"}, + equalsTestCase{int16(0), false, true, "which is not a function"}, + equalsTestCase{int32(0), false, true, "which is not a function"}, + equalsTestCase{int64(0), false, true, "which is not a function"}, + equalsTestCase{uint(0), false, true, "which is not a function"}, + equalsTestCase{uint8(0), false, true, "which is not a function"}, + equalsTestCase{uint16(0), false, true, "which is not a function"}, + equalsTestCase{uint32(0), false, true, "which is not a function"}, + equalsTestCase{uint64(0), false, true, "which is not a function"}, + equalsTestCase{true, false, true, "which is not a function"}, + equalsTestCase{[...]int{}, false, true, "which is not a function"}, + equalsTestCase{map[int]int{}, false, true, "which is not a function"}, + equalsTestCase{&someInt, false, true, "which is not a function"}, + equalsTestCase{[]int{}, false, true, "which is not a function"}, + equalsTestCase{"taco", false, true, "which is not a function"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a function"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// map +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NilMap() { + var nilMap1 map[int]int + var nilMap2 map[int]int + var nilMap3 map[int]uint + var nonNilMap1 map[int]int = make(map[int]int) + var nonNilMap2 map[int]uint = make(map[int]uint) + + matcher := Equals(nilMap1) + ExpectEq("map[]", matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nilMap1, true, false, ""}, + equalsTestCase{nilMap2, true, false, ""}, + equalsTestCase{nilMap3, true, false, ""}, + equalsTestCase{nonNilMap1, false, false, ""}, + equalsTestCase{nonNilMap2, false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a map"}, + equalsTestCase{bool(false), false, true, "which is not a map"}, + equalsTestCase{int(0), false, true, "which is not a map"}, + equalsTestCase{int8(0), false, true, "which is not a map"}, + equalsTestCase{int16(0), false, true, "which is not a map"}, + equalsTestCase{int32(0), false, true, "which is not a map"}, + equalsTestCase{int64(0), false, true, "which is not a map"}, + equalsTestCase{uint(0), false, true, "which is not a map"}, + equalsTestCase{uint8(0), false, true, "which is not a map"}, + equalsTestCase{uint16(0), false, true, "which is not a map"}, + equalsTestCase{uint32(0), false, true, "which is not a map"}, + equalsTestCase{uint64(0), false, true, "which is not a map"}, + equalsTestCase{true, false, true, "which is not a map"}, + equalsTestCase{[...]int{}, false, true, "which is not a map"}, + equalsTestCase{func() {}, false, true, "which is not a map"}, + equalsTestCase{&someInt, false, true, "which is not a map"}, + equalsTestCase{[]int{}, false, true, "which is not a map"}, + equalsTestCase{"taco", false, true, "which is not a map"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a map"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NonNilMap() { + var nilMap1 map[int]int + var nilMap2 map[int]uint + var nonNilMap1 map[int]int = make(map[int]int) + var nonNilMap2 map[int]int = make(map[int]int) + var nonNilMap3 map[int]uint = make(map[int]uint) + + matcher := Equals(nonNilMap1) + ExpectEq("map[]", matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nonNilMap1, true, false, ""}, + equalsTestCase{nonNilMap2, false, false, ""}, + equalsTestCase{nonNilMap3, false, false, ""}, + equalsTestCase{nilMap1, false, false, ""}, + equalsTestCase{nilMap2, false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a map"}, + equalsTestCase{bool(false), false, true, "which is not a map"}, + equalsTestCase{int(0), false, true, "which is not a map"}, + equalsTestCase{int8(0), false, true, "which is not a map"}, + equalsTestCase{int16(0), false, true, "which is not a map"}, + equalsTestCase{int32(0), false, true, "which is not a map"}, + equalsTestCase{int64(0), false, true, "which is not a map"}, + equalsTestCase{uint(0), false, true, "which is not a map"}, + equalsTestCase{uint8(0), false, true, "which is not a map"}, + equalsTestCase{uint16(0), false, true, "which is not a map"}, + equalsTestCase{uint32(0), false, true, "which is not a map"}, + equalsTestCase{uint64(0), false, true, "which is not a map"}, + equalsTestCase{true, false, true, "which is not a map"}, + equalsTestCase{[...]int{}, false, true, "which is not a map"}, + equalsTestCase{func() {}, false, true, "which is not a map"}, + equalsTestCase{&someInt, false, true, "which is not a map"}, + equalsTestCase{[]int{}, false, true, "which is not a map"}, + equalsTestCase{"taco", false, true, "which is not a map"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a map"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Pointers +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NilPointer() { + var someInt int = 17 + var someUint uint = 17 + + var nilInt1 *int + var nilInt2 *int + var nilUint *uint + var nonNilInt *int = &someInt + var nonNilUint *uint = &someUint + + matcher := Equals(nilInt1) + ExpectEq("", matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nilInt1, true, false, ""}, + equalsTestCase{nilInt2, true, false, ""}, + equalsTestCase{nonNilInt, false, false, ""}, + + // Incorrect type. + equalsTestCase{nilUint, false, true, "which is not a *int"}, + equalsTestCase{nonNilUint, false, true, "which is not a *int"}, + + // Other types. + equalsTestCase{0, false, true, "which is not a *int"}, + equalsTestCase{bool(false), false, true, "which is not a *int"}, + equalsTestCase{int(0), false, true, "which is not a *int"}, + equalsTestCase{int8(0), false, true, "which is not a *int"}, + equalsTestCase{int16(0), false, true, "which is not a *int"}, + equalsTestCase{int32(0), false, true, "which is not a *int"}, + equalsTestCase{int64(0), false, true, "which is not a *int"}, + equalsTestCase{uint(0), false, true, "which is not a *int"}, + equalsTestCase{uint8(0), false, true, "which is not a *int"}, + equalsTestCase{uint16(0), false, true, "which is not a *int"}, + equalsTestCase{uint32(0), false, true, "which is not a *int"}, + equalsTestCase{uint64(0), false, true, "which is not a *int"}, + equalsTestCase{true, false, true, "which is not a *int"}, + equalsTestCase{[...]int{}, false, true, "which is not a *int"}, + equalsTestCase{func() {}, false, true, "which is not a *int"}, + equalsTestCase{map[int]int{}, false, true, "which is not a *int"}, + equalsTestCase{[]int{}, false, true, "which is not a *int"}, + equalsTestCase{"taco", false, true, "which is not a *int"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a *int"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NonNilPointer() { + var someInt int = 17 + var someOtherInt int = 17 + var someUint uint = 17 + + var nilInt *int + var nilUint *uint + var nonNilInt1 *int = &someInt + var nonNilInt2 *int = &someOtherInt + var nonNilUint *uint = &someUint + + matcher := Equals(nonNilInt1) + ExpectEq(fmt.Sprintf("%v", nonNilInt1), matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nonNilInt1, true, false, ""}, + equalsTestCase{nonNilInt2, false, false, ""}, + equalsTestCase{nilInt, false, false, ""}, + + // Incorrect type. + equalsTestCase{nilUint, false, true, "which is not a *int"}, + equalsTestCase{nonNilUint, false, true, "which is not a *int"}, + + // Other types. + equalsTestCase{0, false, true, "which is not a *int"}, + equalsTestCase{bool(false), false, true, "which is not a *int"}, + equalsTestCase{int(0), false, true, "which is not a *int"}, + equalsTestCase{int8(0), false, true, "which is not a *int"}, + equalsTestCase{int16(0), false, true, "which is not a *int"}, + equalsTestCase{int32(0), false, true, "which is not a *int"}, + equalsTestCase{int64(0), false, true, "which is not a *int"}, + equalsTestCase{uint(0), false, true, "which is not a *int"}, + equalsTestCase{uint8(0), false, true, "which is not a *int"}, + equalsTestCase{uint16(0), false, true, "which is not a *int"}, + equalsTestCase{uint32(0), false, true, "which is not a *int"}, + equalsTestCase{uint64(0), false, true, "which is not a *int"}, + equalsTestCase{true, false, true, "which is not a *int"}, + equalsTestCase{[...]int{}, false, true, "which is not a *int"}, + equalsTestCase{func() {}, false, true, "which is not a *int"}, + equalsTestCase{map[int]int{}, false, true, "which is not a *int"}, + equalsTestCase{[]int{}, false, true, "which is not a *int"}, + equalsTestCase{"taco", false, true, "which is not a *int"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a *int"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Slices +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NilSlice() { + var nilInt1 []int + var nilInt2 []int + var nilUint []uint + + var nonNilInt []int = make([]int, 0) + var nonNilUint []uint = make([]uint, 0) + + matcher := Equals(nilInt1) + ExpectEq("[]", matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nilInt1, true, false, ""}, + equalsTestCase{nilInt2, true, false, ""}, + equalsTestCase{nonNilInt, false, false, ""}, + + // Incorrect type. + equalsTestCase{nilUint, false, true, "which is not a []int"}, + equalsTestCase{nonNilUint, false, true, "which is not a []int"}, + + // Other types. + equalsTestCase{0, false, true, "which is not a []int"}, + equalsTestCase{bool(false), false, true, "which is not a []int"}, + equalsTestCase{int(0), false, true, "which is not a []int"}, + equalsTestCase{int8(0), false, true, "which is not a []int"}, + equalsTestCase{int16(0), false, true, "which is not a []int"}, + equalsTestCase{int32(0), false, true, "which is not a []int"}, + equalsTestCase{int64(0), false, true, "which is not a []int"}, + equalsTestCase{uint(0), false, true, "which is not a []int"}, + equalsTestCase{uint8(0), false, true, "which is not a []int"}, + equalsTestCase{uint16(0), false, true, "which is not a []int"}, + equalsTestCase{uint32(0), false, true, "which is not a []int"}, + equalsTestCase{uint64(0), false, true, "which is not a []int"}, + equalsTestCase{true, false, true, "which is not a []int"}, + equalsTestCase{[...]int{}, false, true, "which is not a []int"}, + equalsTestCase{func() {}, false, true, "which is not a []int"}, + equalsTestCase{map[int]int{}, false, true, "which is not a []int"}, + equalsTestCase{"taco", false, true, "which is not a []int"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a []int"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NonNilSlice() { + nonNil := make([]int, 0) + f := func() { Equals(nonNil) } + ExpectThat(f, Panics(HasSubstr("non-nil slice"))) +} + +//////////////////////////////////////////////////////////////////////// +// string +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) String() { + partial := "taco" + expected := fmt.Sprintf("%s%d", partial, 1) + + matcher := Equals(expected) + ExpectEq("taco1", matcher.Description()) + + type stringAlias string + + cases := []equalsTestCase{ + // Correct types. + equalsTestCase{"taco1", true, false, ""}, + equalsTestCase{"taco" + "1", true, false, ""}, + equalsTestCase{expected, true, false, ""}, + equalsTestCase{stringAlias("taco1"), true, false, ""}, + + equalsTestCase{"", false, false, ""}, + equalsTestCase{"taco", false, false, ""}, + equalsTestCase{"taco1\x00", false, false, ""}, + equalsTestCase{"taco2", false, false, ""}, + equalsTestCase{stringAlias("taco2"), false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a string"}, + equalsTestCase{bool(false), false, true, "which is not a string"}, + equalsTestCase{int(0), false, true, "which is not a string"}, + equalsTestCase{int8(0), false, true, "which is not a string"}, + equalsTestCase{int16(0), false, true, "which is not a string"}, + equalsTestCase{int32(0), false, true, "which is not a string"}, + equalsTestCase{int64(0), false, true, "which is not a string"}, + equalsTestCase{uint(0), false, true, "which is not a string"}, + equalsTestCase{uint8(0), false, true, "which is not a string"}, + equalsTestCase{uint16(0), false, true, "which is not a string"}, + equalsTestCase{uint32(0), false, true, "which is not a string"}, + equalsTestCase{uint64(0), false, true, "which is not a string"}, + equalsTestCase{true, false, true, "which is not a string"}, + equalsTestCase{[...]int{}, false, true, "which is not a string"}, + equalsTestCase{func() {}, false, true, "which is not a string"}, + equalsTestCase{map[int]int{}, false, true, "which is not a string"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a string"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) StringAlias() { + type stringAlias string + + matcher := Equals(stringAlias("taco")) + ExpectEq("taco", matcher.Description()) + + cases := []equalsTestCase{ + // Correct types. + equalsTestCase{stringAlias("taco"), true, false, ""}, + equalsTestCase{"taco", true, false, ""}, + + equalsTestCase{"burrito", false, false, ""}, + equalsTestCase{stringAlias("burrito"), false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a string"}, + equalsTestCase{bool(false), false, true, "which is not a string"}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// struct +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) Struct() { + type someStruct struct{ foo uint } + f := func() { Equals(someStruct{17}) } + ExpectThat(f, Panics(HasSubstr("unsupported kind struct"))) +} + +//////////////////////////////////////////////////////////////////////// +// unsafe.Pointer +//////////////////////////////////////////////////////////////////////// + +func (t *EqualsTest) NilUnsafePointer() { + someInt := int(17) + + var nilPtr1 unsafe.Pointer + var nilPtr2 unsafe.Pointer + var nonNilPtr unsafe.Pointer = unsafe.Pointer(&someInt) + + matcher := Equals(nilPtr1) + ExpectEq("", matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nilPtr1, true, false, ""}, + equalsTestCase{nilPtr2, true, false, ""}, + equalsTestCase{nonNilPtr, false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{bool(false), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int8(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int16(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int32(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int64(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint8(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint16(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint32(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint64(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{true, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{[...]int{}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{make(chan int), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{func() {}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{map[int]int{}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{&someInt, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{[]int{}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{"taco", false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a unsafe.Pointer"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *EqualsTest) NonNilUnsafePointer() { + someInt := int(17) + someOtherInt := int(17) + + var nilPtr unsafe.Pointer + var nonNilPtr1 unsafe.Pointer = unsafe.Pointer(&someInt) + var nonNilPtr2 unsafe.Pointer = unsafe.Pointer(&someOtherInt) + + matcher := Equals(nonNilPtr1) + ExpectEq(fmt.Sprintf("%v", nonNilPtr1), matcher.Description()) + + cases := []equalsTestCase{ + // Correct type. + equalsTestCase{nonNilPtr1, true, false, ""}, + equalsTestCase{nonNilPtr2, false, false, ""}, + equalsTestCase{nilPtr, false, false, ""}, + + // Other types. + equalsTestCase{0, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{bool(false), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int8(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int16(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int32(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{int64(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint8(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint16(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint32(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{uint64(0), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{true, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{[...]int{}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{make(chan int), false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{func() {}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{map[int]int{}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{&someInt, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{[]int{}, false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{"taco", false, true, "which is not a unsafe.Pointer"}, + equalsTestCase{equalsTestCase{}, false, true, "which is not a unsafe.Pointer"}, + } + + t.checkTestCases(matcher, cases) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/error.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/error.go new file mode 100644 index 0000000..8a078e3 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/error.go @@ -0,0 +1,51 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +// Error returns a matcher that matches non-nil values implementing the +// built-in error interface for whom the return value of Error() matches the +// supplied matcher. +// +// For example: +// +// err := errors.New("taco burrito") +// +// Error(Equals("taco burrito")) // matches err +// Error(HasSubstr("taco")) // matches err +// Error(HasSubstr("enchilada")) // doesn't match err +// +func Error(m Matcher) Matcher { + return &errorMatcher{m} +} + +type errorMatcher struct { + wrappedMatcher Matcher +} + +func (m *errorMatcher) Description() string { + return "error " + m.wrappedMatcher.Description() +} + +func (m *errorMatcher) Matches(c interface{}) error { + // Make sure that c is an error. + e, ok := c.(error) + if !ok { + return NewFatalError("which is not an error") + } + + // Pass on the error text to the wrapped matcher. + return m.wrappedMatcher.Matches(e.Error()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/error_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/error_test.go new file mode 100644 index 0000000..55780a0 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/error_test.go @@ -0,0 +1,92 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "errors" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type ErrorTest struct { + matcherCalled bool + suppliedCandidate interface{} + wrappedError error + + matcher Matcher +} + +func init() { RegisterTestSuite(&ErrorTest{}) } + +func (t *ErrorTest) SetUp(i *TestInfo) { + wrapped := &fakeMatcher{ + func(c interface{}) error { + t.matcherCalled = true + t.suppliedCandidate = c + return t.wrappedError + }, + "is foo", + } + + t.matcher = Error(wrapped) +} + +func isFatal(err error) bool { + _, isFatal := err.(*FatalError) + return isFatal +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *ErrorTest) Description() { + ExpectThat(t.matcher.Description(), Equals("error is foo")) +} + +func (t *ErrorTest) CandidateIsNil() { + err := t.matcher.Matches(nil) + + ExpectThat(t.matcherCalled, Equals(false)) + ExpectThat(err.Error(), Equals("which is not an error")) + ExpectTrue(isFatal(err)) +} + +func (t *ErrorTest) CandidateIsString() { + err := t.matcher.Matches("taco") + + ExpectThat(t.matcherCalled, Equals(false)) + ExpectThat(err.Error(), Equals("which is not an error")) + ExpectTrue(isFatal(err)) +} + +func (t *ErrorTest) CallsWrappedMatcher() { + candidate := errors.New("taco") + t.matcher.Matches(candidate) + + ExpectThat(t.matcherCalled, Equals(true)) + ExpectThat(t.suppliedCandidate, Equals("taco")) +} + +func (t *ErrorTest) ReturnsWrappedMatcherResult() { + t.wrappedError = errors.New("burrito") + err := t.matcher.Matches(errors.New("")) + ExpectThat(err, Equals(t.wrappedError)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go new file mode 100644 index 0000000..4b9d103 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go @@ -0,0 +1,39 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "fmt" + "reflect" +) + +// GreaterOrEqual returns a matcher that matches integer, floating point, or +// strings values v such that v >= x. Comparison is not defined between numeric +// and string types, but is defined between all integer and floating point +// types. +// +// x must itself be an integer, floating point, or string type; otherwise, +// GreaterOrEqual will panic. +func GreaterOrEqual(x interface{}) Matcher { + desc := fmt.Sprintf("greater than or equal to %v", x) + + // Special case: make it clear that strings are strings. + if reflect.TypeOf(x).Kind() == reflect.String { + desc = fmt.Sprintf("greater than or equal to \"%s\"", x) + } + + return transformDescription(Not(LessThan(x)), desc) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal_test.go new file mode 100644 index 0000000..c4fb41b --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal_test.go @@ -0,0 +1,1101 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "math" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type GreaterOrEqualTest struct { +} + +func init() { RegisterTestSuite(&GreaterOrEqualTest{}) } + +type geTestCase struct { + candidate interface{} + expectedResult bool + shouldBeFatal bool + expectedError string +} + +func (t *GreaterOrEqualTest) checkTestCases(matcher Matcher, cases []geTestCase) { + for i, c := range cases { + err := matcher.Matches(c.candidate) + + ExpectThat( + (err == nil), + Equals(c.expectedResult), + "Case %d (candidate %v)", + i, + c.candidate) + + if err == nil { + continue + } + + _, isFatal := err.(*FatalError) + ExpectEq( + c.shouldBeFatal, + isFatal, + "Case %d (candidate %v)", + i, + c.candidate) + + ExpectThat( + err, + Error(Equals(c.expectedError)), + "Case %d (candidate %v)", + i, + c.candidate) + } +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterOrEqualTest) IntegerCandidateBadTypes() { + matcher := GreaterOrEqual(int(-150)) + + cases := []geTestCase{ + geTestCase{true, false, true, "which is not comparable"}, + geTestCase{complex64(-151), false, true, "which is not comparable"}, + geTestCase{complex128(-151), false, true, "which is not comparable"}, + geTestCase{[...]int{-151}, false, true, "which is not comparable"}, + geTestCase{make(chan int), false, true, "which is not comparable"}, + geTestCase{func() {}, false, true, "which is not comparable"}, + geTestCase{map[int]int{}, false, true, "which is not comparable"}, + geTestCase{&geTestCase{}, false, true, "which is not comparable"}, + geTestCase{make([]int, 0), false, true, "which is not comparable"}, + geTestCase{"-151", false, true, "which is not comparable"}, + geTestCase{geTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) FloatCandidateBadTypes() { + matcher := GreaterOrEqual(float32(-150)) + + cases := []geTestCase{ + geTestCase{true, false, true, "which is not comparable"}, + geTestCase{complex64(-151), false, true, "which is not comparable"}, + geTestCase{complex128(-151), false, true, "which is not comparable"}, + geTestCase{[...]int{-151}, false, true, "which is not comparable"}, + geTestCase{make(chan int), false, true, "which is not comparable"}, + geTestCase{func() {}, false, true, "which is not comparable"}, + geTestCase{map[int]int{}, false, true, "which is not comparable"}, + geTestCase{&geTestCase{}, false, true, "which is not comparable"}, + geTestCase{make([]int, 0), false, true, "which is not comparable"}, + geTestCase{"-151", false, true, "which is not comparable"}, + geTestCase{geTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) StringCandidateBadTypes() { + matcher := GreaterOrEqual("17") + + cases := []geTestCase{ + geTestCase{true, false, true, "which is not comparable"}, + geTestCase{int(0), false, true, "which is not comparable"}, + geTestCase{int8(0), false, true, "which is not comparable"}, + geTestCase{int16(0), false, true, "which is not comparable"}, + geTestCase{int32(0), false, true, "which is not comparable"}, + geTestCase{int64(0), false, true, "which is not comparable"}, + geTestCase{uint(0), false, true, "which is not comparable"}, + geTestCase{uint8(0), false, true, "which is not comparable"}, + geTestCase{uint16(0), false, true, "which is not comparable"}, + geTestCase{uint32(0), false, true, "which is not comparable"}, + geTestCase{uint64(0), false, true, "which is not comparable"}, + geTestCase{float32(0), false, true, "which is not comparable"}, + geTestCase{float64(0), false, true, "which is not comparable"}, + geTestCase{complex64(-151), false, true, "which is not comparable"}, + geTestCase{complex128(-151), false, true, "which is not comparable"}, + geTestCase{[...]int{-151}, false, true, "which is not comparable"}, + geTestCase{make(chan int), false, true, "which is not comparable"}, + geTestCase{func() {}, false, true, "which is not comparable"}, + geTestCase{map[int]int{}, false, true, "which is not comparable"}, + geTestCase{&geTestCase{}, false, true, "which is not comparable"}, + geTestCase{make([]int, 0), false, true, "which is not comparable"}, + geTestCase{geTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) BadArgument() { + panicked := false + + defer func() { + ExpectThat(panicked, Equals(true)) + }() + + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + + GreaterOrEqual(complex128(0)) +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterOrEqualTest) NegativeIntegerLiteral() { + matcher := GreaterOrEqual(-150) + desc := matcher.Description() + expectedDesc := "greater than or equal to -150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-(1 << 30), false, false, ""}, + geTestCase{-151, false, false, ""}, + geTestCase{-150, true, false, ""}, + geTestCase{0, true, false, ""}, + geTestCase{17, true, false, ""}, + + geTestCase{int(-(1 << 30)), false, false, ""}, + geTestCase{int(-151), false, false, ""}, + geTestCase{int(-150), true, false, ""}, + geTestCase{int(0), true, false, ""}, + geTestCase{int(17), true, false, ""}, + + geTestCase{int8(-127), true, false, ""}, + geTestCase{int8(0), true, false, ""}, + geTestCase{int8(17), true, false, ""}, + + geTestCase{int16(-(1 << 14)), false, false, ""}, + geTestCase{int16(-151), false, false, ""}, + geTestCase{int16(-150), true, false, ""}, + geTestCase{int16(0), true, false, ""}, + geTestCase{int16(17), true, false, ""}, + + geTestCase{int32(-(1 << 30)), false, false, ""}, + geTestCase{int32(-151), false, false, ""}, + geTestCase{int32(-150), true, false, ""}, + geTestCase{int32(0), true, false, ""}, + geTestCase{int32(17), true, false, ""}, + + geTestCase{int64(-(1 << 30)), false, false, ""}, + geTestCase{int64(-151), false, false, ""}, + geTestCase{int64(-150), true, false, ""}, + geTestCase{int64(0), true, false, ""}, + geTestCase{int64(17), true, false, ""}, + + // Unsigned integers. + geTestCase{uint((1 << 32) - 151), true, false, ""}, + geTestCase{uint(0), true, false, ""}, + geTestCase{uint(17), true, false, ""}, + + geTestCase{uint8(0), true, false, ""}, + geTestCase{uint8(17), true, false, ""}, + geTestCase{uint8(253), true, false, ""}, + + geTestCase{uint16((1 << 16) - 151), true, false, ""}, + geTestCase{uint16(0), true, false, ""}, + geTestCase{uint16(17), true, false, ""}, + + geTestCase{uint32((1 << 32) - 151), true, false, ""}, + geTestCase{uint32(0), true, false, ""}, + geTestCase{uint32(17), true, false, ""}, + + geTestCase{uint64((1 << 64) - 151), true, false, ""}, + geTestCase{uint64(0), true, false, ""}, + geTestCase{uint64(17), true, false, ""}, + + geTestCase{uintptr((1 << 64) - 151), true, false, ""}, + geTestCase{uintptr(0), true, false, ""}, + geTestCase{uintptr(17), true, false, ""}, + + // Floating point. + geTestCase{float32(-(1 << 30)), false, false, ""}, + geTestCase{float32(-151), false, false, ""}, + geTestCase{float32(-150.1), false, false, ""}, + geTestCase{float32(-150), true, false, ""}, + geTestCase{float32(-149.9), true, false, ""}, + geTestCase{float32(0), true, false, ""}, + geTestCase{float32(17), true, false, ""}, + geTestCase{float32(160), true, false, ""}, + + geTestCase{float64(-(1 << 30)), false, false, ""}, + geTestCase{float64(-151), false, false, ""}, + geTestCase{float64(-150.1), false, false, ""}, + geTestCase{float64(-150), true, false, ""}, + geTestCase{float64(-149.9), true, false, ""}, + geTestCase{float64(0), true, false, ""}, + geTestCase{float64(17), true, false, ""}, + geTestCase{float64(160), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) ZeroIntegerLiteral() { + matcher := GreaterOrEqual(0) + desc := matcher.Description() + expectedDesc := "greater than or equal to 0" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-(1 << 30), false, false, ""}, + geTestCase{-1, false, false, ""}, + geTestCase{0, true, false, ""}, + geTestCase{1, true, false, ""}, + geTestCase{17, true, false, ""}, + geTestCase{(1 << 30), true, false, ""}, + + geTestCase{int(-(1 << 30)), false, false, ""}, + geTestCase{int(-1), false, false, ""}, + geTestCase{int(0), true, false, ""}, + geTestCase{int(1), true, false, ""}, + geTestCase{int(17), true, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(0), true, false, ""}, + geTestCase{int8(1), true, false, ""}, + + geTestCase{int16(-(1 << 14)), false, false, ""}, + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(0), true, false, ""}, + geTestCase{int16(1), true, false, ""}, + geTestCase{int16(17), true, false, ""}, + + geTestCase{int32(-(1 << 30)), false, false, ""}, + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(0), true, false, ""}, + geTestCase{int32(1), true, false, ""}, + geTestCase{int32(17), true, false, ""}, + + geTestCase{int64(-(1 << 30)), false, false, ""}, + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(0), true, false, ""}, + geTestCase{int64(1), true, false, ""}, + geTestCase{int64(17), true, false, ""}, + + // Unsigned integers. + geTestCase{uint((1 << 32) - 1), true, false, ""}, + geTestCase{uint(0), true, false, ""}, + geTestCase{uint(17), true, false, ""}, + + geTestCase{uint8(0), true, false, ""}, + geTestCase{uint8(17), true, false, ""}, + geTestCase{uint8(253), true, false, ""}, + + geTestCase{uint16((1 << 16) - 1), true, false, ""}, + geTestCase{uint16(0), true, false, ""}, + geTestCase{uint16(17), true, false, ""}, + + geTestCase{uint32((1 << 32) - 1), true, false, ""}, + geTestCase{uint32(0), true, false, ""}, + geTestCase{uint32(17), true, false, ""}, + + geTestCase{uint64((1 << 64) - 1), true, false, ""}, + geTestCase{uint64(0), true, false, ""}, + geTestCase{uint64(17), true, false, ""}, + + geTestCase{uintptr((1 << 64) - 1), true, false, ""}, + geTestCase{uintptr(0), true, false, ""}, + geTestCase{uintptr(17), true, false, ""}, + + // Floating point. + geTestCase{float32(-(1 << 30)), false, false, ""}, + geTestCase{float32(-1), false, false, ""}, + geTestCase{float32(-0.1), false, false, ""}, + geTestCase{float32(-0.0), true, false, ""}, + geTestCase{float32(0), true, false, ""}, + geTestCase{float32(0.1), true, false, ""}, + geTestCase{float32(17), true, false, ""}, + geTestCase{float32(160), true, false, ""}, + + geTestCase{float64(-(1 << 30)), false, false, ""}, + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(-0.1), false, false, ""}, + geTestCase{float64(-0), true, false, ""}, + geTestCase{float64(0), true, false, ""}, + geTestCase{float64(17), true, false, ""}, + geTestCase{float64(160), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) PositiveIntegerLiteral() { + matcher := GreaterOrEqual(150) + desc := matcher.Description() + expectedDesc := "greater than or equal to 150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-1, false, false, ""}, + geTestCase{149, false, false, ""}, + geTestCase{150, true, false, ""}, + geTestCase{151, true, false, ""}, + + geTestCase{int(-1), false, false, ""}, + geTestCase{int(149), false, false, ""}, + geTestCase{int(150), true, false, ""}, + geTestCase{int(151), true, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(0), false, false, ""}, + geTestCase{int8(17), false, false, ""}, + geTestCase{int8(127), false, false, ""}, + + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(149), false, false, ""}, + geTestCase{int16(150), true, false, ""}, + geTestCase{int16(151), true, false, ""}, + + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(149), false, false, ""}, + geTestCase{int32(150), true, false, ""}, + geTestCase{int32(151), true, false, ""}, + + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(149), false, false, ""}, + geTestCase{int64(150), true, false, ""}, + geTestCase{int64(151), true, false, ""}, + + // Unsigned integers. + geTestCase{uint(0), false, false, ""}, + geTestCase{uint(149), false, false, ""}, + geTestCase{uint(150), true, false, ""}, + geTestCase{uint(151), true, false, ""}, + + geTestCase{uint8(0), false, false, ""}, + geTestCase{uint8(127), false, false, ""}, + + geTestCase{uint16(0), false, false, ""}, + geTestCase{uint16(149), false, false, ""}, + geTestCase{uint16(150), true, false, ""}, + geTestCase{uint16(151), true, false, ""}, + + geTestCase{uint32(0), false, false, ""}, + geTestCase{uint32(149), false, false, ""}, + geTestCase{uint32(150), true, false, ""}, + geTestCase{uint32(151), true, false, ""}, + + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(149), false, false, ""}, + geTestCase{uint64(150), true, false, ""}, + geTestCase{uint64(151), true, false, ""}, + + geTestCase{uintptr(0), false, false, ""}, + geTestCase{uintptr(149), false, false, ""}, + geTestCase{uintptr(150), true, false, ""}, + geTestCase{uintptr(151), true, false, ""}, + + // Floating point. + geTestCase{float32(-1), false, false, ""}, + geTestCase{float32(149), false, false, ""}, + geTestCase{float32(149.9), false, false, ""}, + geTestCase{float32(150), true, false, ""}, + geTestCase{float32(150.1), true, false, ""}, + geTestCase{float32(151), true, false, ""}, + + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(149), false, false, ""}, + geTestCase{float64(149.9), false, false, ""}, + geTestCase{float64(150), true, false, ""}, + geTestCase{float64(150.1), true, false, ""}, + geTestCase{float64(151), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Float literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterOrEqualTest) NegativeFloatLiteral() { + matcher := GreaterOrEqual(-150.1) + desc := matcher.Description() + expectedDesc := "greater than or equal to -150.1" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-(1 << 30), false, false, ""}, + geTestCase{-151, false, false, ""}, + geTestCase{-150, true, false, ""}, + geTestCase{0, true, false, ""}, + geTestCase{17, true, false, ""}, + + geTestCase{int(-(1 << 30)), false, false, ""}, + geTestCase{int(-151), false, false, ""}, + geTestCase{int(-150), true, false, ""}, + geTestCase{int(0), true, false, ""}, + geTestCase{int(17), true, false, ""}, + + geTestCase{int8(-127), true, false, ""}, + geTestCase{int8(0), true, false, ""}, + geTestCase{int8(17), true, false, ""}, + + geTestCase{int16(-(1 << 14)), false, false, ""}, + geTestCase{int16(-151), false, false, ""}, + geTestCase{int16(-150), true, false, ""}, + geTestCase{int16(0), true, false, ""}, + geTestCase{int16(17), true, false, ""}, + + geTestCase{int32(-(1 << 30)), false, false, ""}, + geTestCase{int32(-151), false, false, ""}, + geTestCase{int32(-150), true, false, ""}, + geTestCase{int32(0), true, false, ""}, + geTestCase{int32(17), true, false, ""}, + + geTestCase{int64(-(1 << 30)), false, false, ""}, + geTestCase{int64(-151), false, false, ""}, + geTestCase{int64(-150), true, false, ""}, + geTestCase{int64(0), true, false, ""}, + geTestCase{int64(17), true, false, ""}, + + // Unsigned integers. + geTestCase{uint((1 << 32) - 151), true, false, ""}, + geTestCase{uint(0), true, false, ""}, + geTestCase{uint(17), true, false, ""}, + + geTestCase{uint8(0), true, false, ""}, + geTestCase{uint8(17), true, false, ""}, + geTestCase{uint8(253), true, false, ""}, + + geTestCase{uint16((1 << 16) - 151), true, false, ""}, + geTestCase{uint16(0), true, false, ""}, + geTestCase{uint16(17), true, false, ""}, + + geTestCase{uint32((1 << 32) - 151), true, false, ""}, + geTestCase{uint32(0), true, false, ""}, + geTestCase{uint32(17), true, false, ""}, + + geTestCase{uint64((1 << 64) - 151), true, false, ""}, + geTestCase{uint64(0), true, false, ""}, + geTestCase{uint64(17), true, false, ""}, + + geTestCase{uintptr((1 << 64) - 151), true, false, ""}, + geTestCase{uintptr(0), true, false, ""}, + geTestCase{uintptr(17), true, false, ""}, + + // Floating point. + geTestCase{float32(-(1 << 30)), false, false, ""}, + geTestCase{float32(-151), false, false, ""}, + geTestCase{float32(-150.2), false, false, ""}, + geTestCase{float32(-150.1), true, false, ""}, + geTestCase{float32(-150), true, false, ""}, + geTestCase{float32(0), true, false, ""}, + geTestCase{float32(17), true, false, ""}, + geTestCase{float32(160), true, false, ""}, + + geTestCase{float64(-(1 << 30)), false, false, ""}, + geTestCase{float64(-151), false, false, ""}, + geTestCase{float64(-150.2), false, false, ""}, + geTestCase{float64(-150.1), true, false, ""}, + geTestCase{float64(-150), true, false, ""}, + geTestCase{float64(0), true, false, ""}, + geTestCase{float64(17), true, false, ""}, + geTestCase{float64(160), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) PositiveFloatLiteral() { + matcher := GreaterOrEqual(149.9) + desc := matcher.Description() + expectedDesc := "greater than or equal to 149.9" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-1, false, false, ""}, + geTestCase{149, false, false, ""}, + geTestCase{150, true, false, ""}, + geTestCase{151, true, false, ""}, + + geTestCase{int(-1), false, false, ""}, + geTestCase{int(149), false, false, ""}, + geTestCase{int(150), true, false, ""}, + geTestCase{int(151), true, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(0), false, false, ""}, + geTestCase{int8(17), false, false, ""}, + geTestCase{int8(127), false, false, ""}, + + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(149), false, false, ""}, + geTestCase{int16(150), true, false, ""}, + geTestCase{int16(151), true, false, ""}, + + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(149), false, false, ""}, + geTestCase{int32(150), true, false, ""}, + geTestCase{int32(151), true, false, ""}, + + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(149), false, false, ""}, + geTestCase{int64(150), true, false, ""}, + geTestCase{int64(151), true, false, ""}, + + // Unsigned integers. + geTestCase{uint(0), false, false, ""}, + geTestCase{uint(149), false, false, ""}, + geTestCase{uint(150), true, false, ""}, + geTestCase{uint(151), true, false, ""}, + + geTestCase{uint8(0), false, false, ""}, + geTestCase{uint8(127), false, false, ""}, + + geTestCase{uint16(0), false, false, ""}, + geTestCase{uint16(149), false, false, ""}, + geTestCase{uint16(150), true, false, ""}, + geTestCase{uint16(151), true, false, ""}, + + geTestCase{uint32(0), false, false, ""}, + geTestCase{uint32(149), false, false, ""}, + geTestCase{uint32(150), true, false, ""}, + geTestCase{uint32(151), true, false, ""}, + + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(149), false, false, ""}, + geTestCase{uint64(150), true, false, ""}, + geTestCase{uint64(151), true, false, ""}, + + geTestCase{uintptr(0), false, false, ""}, + geTestCase{uintptr(149), false, false, ""}, + geTestCase{uintptr(150), true, false, ""}, + geTestCase{uintptr(151), true, false, ""}, + + // Floating point. + geTestCase{float32(-1), false, false, ""}, + geTestCase{float32(149), false, false, ""}, + geTestCase{float32(149.8), false, false, ""}, + geTestCase{float32(149.9), true, false, ""}, + geTestCase{float32(150), true, false, ""}, + geTestCase{float32(151), true, false, ""}, + + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(149), false, false, ""}, + geTestCase{float64(149.8), false, false, ""}, + geTestCase{float64(149.9), true, false, ""}, + geTestCase{float64(150), true, false, ""}, + geTestCase{float64(151), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Subtle cases +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterOrEqualTest) Int64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := GreaterOrEqual(int64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than or equal to 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-1, false, false, ""}, + geTestCase{kTwoTo25 + 0, false, false, ""}, + geTestCase{kTwoTo25 + 1, true, false, ""}, + geTestCase{kTwoTo25 + 2, true, false, ""}, + + geTestCase{int(-1), false, false, ""}, + geTestCase{int(kTwoTo25 + 0), false, false, ""}, + geTestCase{int(kTwoTo25 + 1), true, false, ""}, + geTestCase{int(kTwoTo25 + 2), true, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(127), false, false, ""}, + + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(0), false, false, ""}, + geTestCase{int16(32767), false, false, ""}, + + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(kTwoTo25 + 0), false, false, ""}, + geTestCase{int32(kTwoTo25 + 1), true, false, ""}, + geTestCase{int32(kTwoTo25 + 2), true, false, ""}, + + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(kTwoTo25 + 0), false, false, ""}, + geTestCase{int64(kTwoTo25 + 1), true, false, ""}, + geTestCase{int64(kTwoTo25 + 2), true, false, ""}, + + // Unsigned integers. + geTestCase{uint(0), false, false, ""}, + geTestCase{uint(kTwoTo25 + 0), false, false, ""}, + geTestCase{uint(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint(kTwoTo25 + 2), true, false, ""}, + + geTestCase{uint8(0), false, false, ""}, + geTestCase{uint8(255), false, false, ""}, + + geTestCase{uint16(0), false, false, ""}, + geTestCase{uint16(65535), false, false, ""}, + + geTestCase{uint32(0), false, false, ""}, + geTestCase{uint32(kTwoTo25 + 0), false, false, ""}, + geTestCase{uint32(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint32(kTwoTo25 + 2), true, false, ""}, + + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + geTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + + geTestCase{uintptr(0), false, false, ""}, + geTestCase{uintptr(kTwoTo25 + 0), false, false, ""}, + geTestCase{uintptr(kTwoTo25 + 1), true, false, ""}, + geTestCase{uintptr(kTwoTo25 + 2), true, false, ""}, + + // Floating point. + geTestCase{float32(-1), false, false, ""}, + geTestCase{float32(kTwoTo25 - 2), false, false, ""}, + geTestCase{float32(kTwoTo25 - 1), true, false, ""}, + geTestCase{float32(kTwoTo25 + 0), true, false, ""}, + geTestCase{float32(kTwoTo25 + 1), true, false, ""}, + geTestCase{float32(kTwoTo25 + 2), true, false, ""}, + geTestCase{float32(kTwoTo25 + 3), true, false, ""}, + + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(kTwoTo25 - 2), false, false, ""}, + geTestCase{float64(kTwoTo25 - 1), false, false, ""}, + geTestCase{float64(kTwoTo25 + 0), false, false, ""}, + geTestCase{float64(kTwoTo25 + 1), true, false, ""}, + geTestCase{float64(kTwoTo25 + 2), true, false, ""}, + geTestCase{float64(kTwoTo25 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) Int64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := GreaterOrEqual(int64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than or equal to 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-1, false, false, ""}, + geTestCase{1 << 30, false, false, ""}, + + geTestCase{int(-1), false, false, ""}, + geTestCase{int(math.MaxInt32), false, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(127), false, false, ""}, + + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(0), false, false, ""}, + geTestCase{int16(32767), false, false, ""}, + + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(math.MaxInt32), false, false, ""}, + + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(kTwoTo54 - 1), false, false, ""}, + geTestCase{int64(kTwoTo54 + 0), false, false, ""}, + geTestCase{int64(kTwoTo54 + 1), true, false, ""}, + geTestCase{int64(kTwoTo54 + 2), true, false, ""}, + + // Unsigned integers. + geTestCase{uint(0), false, false, ""}, + geTestCase{uint(math.MaxUint32), false, false, ""}, + + geTestCase{uint8(0), false, false, ""}, + geTestCase{uint8(255), false, false, ""}, + + geTestCase{uint16(0), false, false, ""}, + geTestCase{uint16(65535), false, false, ""}, + + geTestCase{uint32(0), false, false, ""}, + geTestCase{uint32(math.MaxUint32), false, false, ""}, + + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(kTwoTo54 - 1), false, false, ""}, + geTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + geTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + geTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + + geTestCase{uintptr(0), false, false, ""}, + geTestCase{uintptr(kTwoTo54 - 1), false, false, ""}, + geTestCase{uintptr(kTwoTo54 + 0), false, false, ""}, + geTestCase{uintptr(kTwoTo54 + 1), true, false, ""}, + geTestCase{uintptr(kTwoTo54 + 2), true, false, ""}, + + // Floating point. + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(kTwoTo54 - 2), false, false, ""}, + geTestCase{float64(kTwoTo54 - 1), true, false, ""}, + geTestCase{float64(kTwoTo54 + 0), true, false, ""}, + geTestCase{float64(kTwoTo54 + 1), true, false, ""}, + geTestCase{float64(kTwoTo54 + 2), true, false, ""}, + geTestCase{float64(kTwoTo54 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) Uint64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := GreaterOrEqual(uint64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than or equal to 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-1, false, false, ""}, + geTestCase{kTwoTo25 + 0, false, false, ""}, + geTestCase{kTwoTo25 + 1, true, false, ""}, + geTestCase{kTwoTo25 + 2, true, false, ""}, + + geTestCase{int(-1), false, false, ""}, + geTestCase{int(kTwoTo25 + 0), false, false, ""}, + geTestCase{int(kTwoTo25 + 1), true, false, ""}, + geTestCase{int(kTwoTo25 + 2), true, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(127), false, false, ""}, + + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(0), false, false, ""}, + geTestCase{int16(32767), false, false, ""}, + + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(kTwoTo25 + 0), false, false, ""}, + geTestCase{int32(kTwoTo25 + 1), true, false, ""}, + geTestCase{int32(kTwoTo25 + 2), true, false, ""}, + + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(kTwoTo25 + 0), false, false, ""}, + geTestCase{int64(kTwoTo25 + 1), true, false, ""}, + geTestCase{int64(kTwoTo25 + 2), true, false, ""}, + + // Unsigned integers. + geTestCase{uint(0), false, false, ""}, + geTestCase{uint(kTwoTo25 + 0), false, false, ""}, + geTestCase{uint(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint(kTwoTo25 + 2), true, false, ""}, + + geTestCase{uint8(0), false, false, ""}, + geTestCase{uint8(255), false, false, ""}, + + geTestCase{uint16(0), false, false, ""}, + geTestCase{uint16(65535), false, false, ""}, + + geTestCase{uint32(0), false, false, ""}, + geTestCase{uint32(kTwoTo25 + 0), false, false, ""}, + geTestCase{uint32(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint32(kTwoTo25 + 2), true, false, ""}, + + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + geTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + + geTestCase{uintptr(0), false, false, ""}, + geTestCase{uintptr(kTwoTo25 + 0), false, false, ""}, + geTestCase{uintptr(kTwoTo25 + 1), true, false, ""}, + geTestCase{uintptr(kTwoTo25 + 2), true, false, ""}, + + // Floating point. + geTestCase{float32(-1), false, false, ""}, + geTestCase{float32(kTwoTo25 - 2), false, false, ""}, + geTestCase{float32(kTwoTo25 - 1), true, false, ""}, + geTestCase{float32(kTwoTo25 + 0), true, false, ""}, + geTestCase{float32(kTwoTo25 + 1), true, false, ""}, + geTestCase{float32(kTwoTo25 + 2), true, false, ""}, + geTestCase{float32(kTwoTo25 + 3), true, false, ""}, + + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(kTwoTo25 - 2), false, false, ""}, + geTestCase{float64(kTwoTo25 - 1), false, false, ""}, + geTestCase{float64(kTwoTo25 + 0), false, false, ""}, + geTestCase{float64(kTwoTo25 + 1), true, false, ""}, + geTestCase{float64(kTwoTo25 + 2), true, false, ""}, + geTestCase{float64(kTwoTo25 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) Uint64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := GreaterOrEqual(uint64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than or equal to 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{-1, false, false, ""}, + geTestCase{1 << 30, false, false, ""}, + + geTestCase{int(-1), false, false, ""}, + geTestCase{int(math.MaxInt32), false, false, ""}, + + geTestCase{int8(-1), false, false, ""}, + geTestCase{int8(127), false, false, ""}, + + geTestCase{int16(-1), false, false, ""}, + geTestCase{int16(0), false, false, ""}, + geTestCase{int16(32767), false, false, ""}, + + geTestCase{int32(-1), false, false, ""}, + geTestCase{int32(math.MaxInt32), false, false, ""}, + + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(kTwoTo54 - 1), false, false, ""}, + geTestCase{int64(kTwoTo54 + 0), false, false, ""}, + geTestCase{int64(kTwoTo54 + 1), true, false, ""}, + geTestCase{int64(kTwoTo54 + 2), true, false, ""}, + + // Unsigned integers. + geTestCase{uint(0), false, false, ""}, + geTestCase{uint(math.MaxUint32), false, false, ""}, + + geTestCase{uint8(0), false, false, ""}, + geTestCase{uint8(255), false, false, ""}, + + geTestCase{uint16(0), false, false, ""}, + geTestCase{uint16(65535), false, false, ""}, + + geTestCase{uint32(0), false, false, ""}, + geTestCase{uint32(math.MaxUint32), false, false, ""}, + + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(kTwoTo54 - 1), false, false, ""}, + geTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + geTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + geTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + + geTestCase{uintptr(0), false, false, ""}, + geTestCase{uintptr(kTwoTo54 - 1), false, false, ""}, + geTestCase{uintptr(kTwoTo54 + 0), false, false, ""}, + geTestCase{uintptr(kTwoTo54 + 1), true, false, ""}, + geTestCase{uintptr(kTwoTo54 + 2), true, false, ""}, + + // Floating point. + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(kTwoTo54 - 2), false, false, ""}, + geTestCase{float64(kTwoTo54 - 1), true, false, ""}, + geTestCase{float64(kTwoTo54 + 0), true, false, ""}, + geTestCase{float64(kTwoTo54 + 1), true, false, ""}, + geTestCase{float64(kTwoTo54 + 2), true, false, ""}, + geTestCase{float64(kTwoTo54 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) Float32AboveExactIntegerRange() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := GreaterOrEqual(float32(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than or equal to 3.3554432e+07" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(kTwoTo25 - 2), false, false, ""}, + geTestCase{int64(kTwoTo25 - 1), true, false, ""}, + geTestCase{int64(kTwoTo25 + 0), true, false, ""}, + geTestCase{int64(kTwoTo25 + 1), true, false, ""}, + geTestCase{int64(kTwoTo25 + 2), true, false, ""}, + geTestCase{int64(kTwoTo25 + 3), true, false, ""}, + + // Unsigned integers. + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(kTwoTo25 - 2), false, false, ""}, + geTestCase{uint64(kTwoTo25 - 1), true, false, ""}, + geTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + geTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + geTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + geTestCase{uint64(kTwoTo25 + 3), true, false, ""}, + + // Floating point. + geTestCase{float32(-1), false, false, ""}, + geTestCase{float32(kTwoTo25 - 2), false, false, ""}, + geTestCase{float32(kTwoTo25 - 1), true, false, ""}, + geTestCase{float32(kTwoTo25 + 0), true, false, ""}, + geTestCase{float32(kTwoTo25 + 1), true, false, ""}, + geTestCase{float32(kTwoTo25 + 2), true, false, ""}, + geTestCase{float32(kTwoTo25 + 3), true, false, ""}, + + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(kTwoTo25 - 2), false, false, ""}, + geTestCase{float64(kTwoTo25 - 1), true, false, ""}, + geTestCase{float64(kTwoTo25 + 0), true, false, ""}, + geTestCase{float64(kTwoTo25 + 1), true, false, ""}, + geTestCase{float64(kTwoTo25 + 2), true, false, ""}, + geTestCase{float64(kTwoTo25 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) Float64AboveExactIntegerRange() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := GreaterOrEqual(float64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than or equal to 1.8014398509481984e+16" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + // Signed integers. + geTestCase{int64(-1), false, false, ""}, + geTestCase{int64(kTwoTo54 - 2), false, false, ""}, + geTestCase{int64(kTwoTo54 - 1), true, false, ""}, + geTestCase{int64(kTwoTo54 + 0), true, false, ""}, + geTestCase{int64(kTwoTo54 + 1), true, false, ""}, + geTestCase{int64(kTwoTo54 + 2), true, false, ""}, + geTestCase{int64(kTwoTo54 + 3), true, false, ""}, + + // Unsigned integers. + geTestCase{uint64(0), false, false, ""}, + geTestCase{uint64(kTwoTo54 - 2), false, false, ""}, + geTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + geTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + geTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + geTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + geTestCase{uint64(kTwoTo54 + 3), true, false, ""}, + + // Floating point. + geTestCase{float64(-1), false, false, ""}, + geTestCase{float64(kTwoTo54 - 2), false, false, ""}, + geTestCase{float64(kTwoTo54 - 1), true, false, ""}, + geTestCase{float64(kTwoTo54 + 0), true, false, ""}, + geTestCase{float64(kTwoTo54 + 1), true, false, ""}, + geTestCase{float64(kTwoTo54 + 2), true, false, ""}, + geTestCase{float64(kTwoTo54 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// String literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterOrEqualTest) EmptyString() { + matcher := GreaterOrEqual("") + desc := matcher.Description() + expectedDesc := "greater than or equal to \"\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + geTestCase{"", true, false, ""}, + geTestCase{"\x00", true, false, ""}, + geTestCase{"a", true, false, ""}, + geTestCase{"foo", true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) SingleNullByte() { + matcher := GreaterOrEqual("\x00") + desc := matcher.Description() + expectedDesc := "greater than or equal to \"\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + geTestCase{"", false, false, ""}, + geTestCase{"\x00", true, false, ""}, + geTestCase{"a", true, false, ""}, + geTestCase{"foo", true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterOrEqualTest) LongerString() { + matcher := GreaterOrEqual("foo\x00") + desc := matcher.Description() + expectedDesc := "greater than or equal to \"foo\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []geTestCase{ + geTestCase{"", false, false, ""}, + geTestCase{"\x00", false, false, ""}, + geTestCase{"bar", false, false, ""}, + geTestCase{"foo", false, false, ""}, + geTestCase{"foo\x00", true, false, ""}, + geTestCase{"fooa", true, false, ""}, + geTestCase{"qux", true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go new file mode 100644 index 0000000..3eef321 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go @@ -0,0 +1,39 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "fmt" + "reflect" +) + +// GreaterThan returns a matcher that matches integer, floating point, or +// strings values v such that v > x. Comparison is not defined between numeric +// and string types, but is defined between all integer and floating point +// types. +// +// x must itself be an integer, floating point, or string type; otherwise, +// GreaterThan will panic. +func GreaterThan(x interface{}) Matcher { + desc := fmt.Sprintf("greater than %v", x) + + // Special case: make it clear that strings are strings. + if reflect.TypeOf(x).Kind() == reflect.String { + desc = fmt.Sprintf("greater than \"%s\"", x) + } + + return transformDescription(Not(LessOrEqual(x)), desc) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than_test.go new file mode 100644 index 0000000..1235d68 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than_test.go @@ -0,0 +1,1077 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "math" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type GreaterThanTest struct { +} + +func init() { RegisterTestSuite(&GreaterThanTest{}) } + +type gtTestCase struct { + candidate interface{} + expectedResult bool + shouldBeFatal bool + expectedError string +} + +func (t *GreaterThanTest) checkTestCases(matcher Matcher, cases []gtTestCase) { + for i, c := range cases { + err := matcher.Matches(c.candidate) + + ExpectThat( + (err == nil), + Equals(c.expectedResult), + "Case %d (candidate %v)", + i, + c.candidate) + + if err == nil { + continue + } + + _, isFatal := err.(*FatalError) + ExpectEq( + c.shouldBeFatal, + isFatal, + "Case %d (candidate %v)", + i, + c.candidate) + + ExpectThat( + err, + Error(Equals(c.expectedError)), + "Case %d (candidate %v)", + i, + c.candidate) + } +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterThanTest) IntegerCandidateBadTypes() { + matcher := GreaterThan(int(-150)) + + cases := []gtTestCase{ + gtTestCase{true, false, true, "which is not comparable"}, + gtTestCase{complex64(-151), false, true, "which is not comparable"}, + gtTestCase{complex128(-151), false, true, "which is not comparable"}, + gtTestCase{[...]int{-151}, false, true, "which is not comparable"}, + gtTestCase{make(chan int), false, true, "which is not comparable"}, + gtTestCase{func() {}, false, true, "which is not comparable"}, + gtTestCase{map[int]int{}, false, true, "which is not comparable"}, + gtTestCase{>TestCase{}, false, true, "which is not comparable"}, + gtTestCase{make([]int, 0), false, true, "which is not comparable"}, + gtTestCase{"-151", false, true, "which is not comparable"}, + gtTestCase{gtTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) FloatCandidateBadTypes() { + matcher := GreaterThan(float32(-150)) + + cases := []gtTestCase{ + gtTestCase{true, false, true, "which is not comparable"}, + gtTestCase{complex64(-151), false, true, "which is not comparable"}, + gtTestCase{complex128(-151), false, true, "which is not comparable"}, + gtTestCase{[...]int{-151}, false, true, "which is not comparable"}, + gtTestCase{make(chan int), false, true, "which is not comparable"}, + gtTestCase{func() {}, false, true, "which is not comparable"}, + gtTestCase{map[int]int{}, false, true, "which is not comparable"}, + gtTestCase{>TestCase{}, false, true, "which is not comparable"}, + gtTestCase{make([]int, 0), false, true, "which is not comparable"}, + gtTestCase{"-151", false, true, "which is not comparable"}, + gtTestCase{gtTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) StringCandidateBadTypes() { + matcher := GreaterThan("17") + + cases := []gtTestCase{ + gtTestCase{true, false, true, "which is not comparable"}, + gtTestCase{int(0), false, true, "which is not comparable"}, + gtTestCase{int8(0), false, true, "which is not comparable"}, + gtTestCase{int16(0), false, true, "which is not comparable"}, + gtTestCase{int32(0), false, true, "which is not comparable"}, + gtTestCase{int64(0), false, true, "which is not comparable"}, + gtTestCase{uint(0), false, true, "which is not comparable"}, + gtTestCase{uint8(0), false, true, "which is not comparable"}, + gtTestCase{uint16(0), false, true, "which is not comparable"}, + gtTestCase{uint32(0), false, true, "which is not comparable"}, + gtTestCase{uint64(0), false, true, "which is not comparable"}, + gtTestCase{float32(0), false, true, "which is not comparable"}, + gtTestCase{float64(0), false, true, "which is not comparable"}, + gtTestCase{complex64(-151), false, true, "which is not comparable"}, + gtTestCase{complex128(-151), false, true, "which is not comparable"}, + gtTestCase{[...]int{-151}, false, true, "which is not comparable"}, + gtTestCase{make(chan int), false, true, "which is not comparable"}, + gtTestCase{func() {}, false, true, "which is not comparable"}, + gtTestCase{map[int]int{}, false, true, "which is not comparable"}, + gtTestCase{>TestCase{}, false, true, "which is not comparable"}, + gtTestCase{make([]int, 0), false, true, "which is not comparable"}, + gtTestCase{gtTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) BadArgument() { + panicked := false + + defer func() { + ExpectThat(panicked, Equals(true)) + }() + + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + + GreaterThan(complex128(0)) +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterThanTest) NegativeIntegerLiteral() { + matcher := GreaterThan(-150) + desc := matcher.Description() + expectedDesc := "greater than -150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-(1 << 30), false, false, ""}, + gtTestCase{-151, false, false, ""}, + gtTestCase{-150, false, false, ""}, + gtTestCase{-149, true, false, ""}, + gtTestCase{0, true, false, ""}, + gtTestCase{17, true, false, ""}, + + gtTestCase{int(-(1 << 30)), false, false, ""}, + gtTestCase{int(-151), false, false, ""}, + gtTestCase{int(-150), false, false, ""}, + gtTestCase{int(-149), true, false, ""}, + gtTestCase{int(0), true, false, ""}, + gtTestCase{int(17), true, false, ""}, + + gtTestCase{int8(-127), true, false, ""}, + gtTestCase{int8(0), true, false, ""}, + gtTestCase{int8(17), true, false, ""}, + + gtTestCase{int16(-(1 << 14)), false, false, ""}, + gtTestCase{int16(-151), false, false, ""}, + gtTestCase{int16(-150), false, false, ""}, + gtTestCase{int16(-149), true, false, ""}, + gtTestCase{int16(0), true, false, ""}, + gtTestCase{int16(17), true, false, ""}, + + gtTestCase{int32(-(1 << 30)), false, false, ""}, + gtTestCase{int32(-151), false, false, ""}, + gtTestCase{int32(-150), false, false, ""}, + gtTestCase{int32(-149), true, false, ""}, + gtTestCase{int32(0), true, false, ""}, + gtTestCase{int32(17), true, false, ""}, + + gtTestCase{int64(-(1 << 30)), false, false, ""}, + gtTestCase{int64(-151), false, false, ""}, + gtTestCase{int64(-150), false, false, ""}, + gtTestCase{int64(-149), true, false, ""}, + gtTestCase{int64(0), true, false, ""}, + gtTestCase{int64(17), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint((1 << 32) - 151), true, false, ""}, + gtTestCase{uint(0), true, false, ""}, + gtTestCase{uint(17), true, false, ""}, + + gtTestCase{uint8(0), true, false, ""}, + gtTestCase{uint8(17), true, false, ""}, + gtTestCase{uint8(253), true, false, ""}, + + gtTestCase{uint16((1 << 16) - 151), true, false, ""}, + gtTestCase{uint16(0), true, false, ""}, + gtTestCase{uint16(17), true, false, ""}, + + gtTestCase{uint32((1 << 32) - 151), true, false, ""}, + gtTestCase{uint32(0), true, false, ""}, + gtTestCase{uint32(17), true, false, ""}, + + gtTestCase{uint64((1 << 64) - 151), true, false, ""}, + gtTestCase{uint64(0), true, false, ""}, + gtTestCase{uint64(17), true, false, ""}, + + // Floating point. + gtTestCase{float32(-(1 << 30)), false, false, ""}, + gtTestCase{float32(-151), false, false, ""}, + gtTestCase{float32(-150.1), false, false, ""}, + gtTestCase{float32(-150), false, false, ""}, + gtTestCase{float32(-149.9), true, false, ""}, + gtTestCase{float32(0), true, false, ""}, + gtTestCase{float32(17), true, false, ""}, + gtTestCase{float32(160), true, false, ""}, + + gtTestCase{float64(-(1 << 30)), false, false, ""}, + gtTestCase{float64(-151), false, false, ""}, + gtTestCase{float64(-150.1), false, false, ""}, + gtTestCase{float64(-150), false, false, ""}, + gtTestCase{float64(-149.9), true, false, ""}, + gtTestCase{float64(0), true, false, ""}, + gtTestCase{float64(17), true, false, ""}, + gtTestCase{float64(160), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) ZeroIntegerLiteral() { + matcher := GreaterThan(0) + desc := matcher.Description() + expectedDesc := "greater than 0" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-(1 << 30), false, false, ""}, + gtTestCase{-1, false, false, ""}, + gtTestCase{0, false, false, ""}, + gtTestCase{1, true, false, ""}, + gtTestCase{17, true, false, ""}, + gtTestCase{(1 << 30), true, false, ""}, + + gtTestCase{int(-(1 << 30)), false, false, ""}, + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(0), false, false, ""}, + gtTestCase{int(1), true, false, ""}, + gtTestCase{int(17), true, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(0), false, false, ""}, + gtTestCase{int8(1), true, false, ""}, + + gtTestCase{int16(-(1 << 14)), false, false, ""}, + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(0), false, false, ""}, + gtTestCase{int16(1), true, false, ""}, + gtTestCase{int16(17), true, false, ""}, + + gtTestCase{int32(-(1 << 30)), false, false, ""}, + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(0), false, false, ""}, + gtTestCase{int32(1), true, false, ""}, + gtTestCase{int32(17), true, false, ""}, + + gtTestCase{int64(-(1 << 30)), false, false, ""}, + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(0), false, false, ""}, + gtTestCase{int64(1), true, false, ""}, + gtTestCase{int64(17), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint((1 << 32) - 1), true, false, ""}, + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(1), true, false, ""}, + gtTestCase{uint(17), true, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(1), true, false, ""}, + gtTestCase{uint8(17), true, false, ""}, + gtTestCase{uint8(253), true, false, ""}, + + gtTestCase{uint16((1 << 16) - 1), true, false, ""}, + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(1), true, false, ""}, + gtTestCase{uint16(17), true, false, ""}, + + gtTestCase{uint32((1 << 32) - 1), true, false, ""}, + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(1), true, false, ""}, + gtTestCase{uint32(17), true, false, ""}, + + gtTestCase{uint64((1 << 64) - 1), true, false, ""}, + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(1), true, false, ""}, + gtTestCase{uint64(17), true, false, ""}, + + // Floating point. + gtTestCase{float32(-(1 << 30)), false, false, ""}, + gtTestCase{float32(-1), false, false, ""}, + gtTestCase{float32(-0.1), false, false, ""}, + gtTestCase{float32(-0.0), false, false, ""}, + gtTestCase{float32(0), false, false, ""}, + gtTestCase{float32(0.1), true, false, ""}, + gtTestCase{float32(17), true, false, ""}, + gtTestCase{float32(160), true, false, ""}, + + gtTestCase{float64(-(1 << 30)), false, false, ""}, + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(-0.1), false, false, ""}, + gtTestCase{float64(-0), false, false, ""}, + gtTestCase{float64(0), false, false, ""}, + gtTestCase{float64(0.1), true, false, ""}, + gtTestCase{float64(17), true, false, ""}, + gtTestCase{float64(160), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) PositiveIntegerLiteral() { + matcher := GreaterThan(150) + desc := matcher.Description() + expectedDesc := "greater than 150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-1, false, false, ""}, + gtTestCase{149, false, false, ""}, + gtTestCase{150, false, false, ""}, + gtTestCase{151, true, false, ""}, + + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(149), false, false, ""}, + gtTestCase{int(150), false, false, ""}, + gtTestCase{int(151), true, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(0), false, false, ""}, + gtTestCase{int8(17), false, false, ""}, + gtTestCase{int8(127), false, false, ""}, + + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(149), false, false, ""}, + gtTestCase{int16(150), false, false, ""}, + gtTestCase{int16(151), true, false, ""}, + + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(149), false, false, ""}, + gtTestCase{int32(150), false, false, ""}, + gtTestCase{int32(151), true, false, ""}, + + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(149), false, false, ""}, + gtTestCase{int64(150), false, false, ""}, + gtTestCase{int64(151), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(149), false, false, ""}, + gtTestCase{uint(150), false, false, ""}, + gtTestCase{uint(151), true, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(127), false, false, ""}, + + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(149), false, false, ""}, + gtTestCase{uint16(150), false, false, ""}, + gtTestCase{uint16(151), true, false, ""}, + + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(149), false, false, ""}, + gtTestCase{uint32(150), false, false, ""}, + gtTestCase{uint32(151), true, false, ""}, + + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(149), false, false, ""}, + gtTestCase{uint64(150), false, false, ""}, + gtTestCase{uint64(151), true, false, ""}, + + // Floating point. + gtTestCase{float32(-1), false, false, ""}, + gtTestCase{float32(149), false, false, ""}, + gtTestCase{float32(149.9), false, false, ""}, + gtTestCase{float32(150), false, false, ""}, + gtTestCase{float32(150.1), true, false, ""}, + gtTestCase{float32(151), true, false, ""}, + + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(149), false, false, ""}, + gtTestCase{float64(149.9), false, false, ""}, + gtTestCase{float64(150), false, false, ""}, + gtTestCase{float64(150.1), true, false, ""}, + gtTestCase{float64(151), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Float literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterThanTest) NegativeFloatLiteral() { + matcher := GreaterThan(-150.1) + desc := matcher.Description() + expectedDesc := "greater than -150.1" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-(1 << 30), false, false, ""}, + gtTestCase{-151, false, false, ""}, + gtTestCase{-150.1, false, false, ""}, + gtTestCase{-150, true, false, ""}, + gtTestCase{-149, true, false, ""}, + gtTestCase{0, true, false, ""}, + gtTestCase{17, true, false, ""}, + + gtTestCase{int(-(1 << 30)), false, false, ""}, + gtTestCase{int(-151), false, false, ""}, + gtTestCase{int(-150), true, false, ""}, + gtTestCase{int(-149), true, false, ""}, + gtTestCase{int(0), true, false, ""}, + gtTestCase{int(17), true, false, ""}, + + gtTestCase{int8(-127), true, false, ""}, + gtTestCase{int8(0), true, false, ""}, + gtTestCase{int8(17), true, false, ""}, + + gtTestCase{int16(-(1 << 14)), false, false, ""}, + gtTestCase{int16(-151), false, false, ""}, + gtTestCase{int16(-150), true, false, ""}, + gtTestCase{int16(-149), true, false, ""}, + gtTestCase{int16(0), true, false, ""}, + gtTestCase{int16(17), true, false, ""}, + + gtTestCase{int32(-(1 << 30)), false, false, ""}, + gtTestCase{int32(-151), false, false, ""}, + gtTestCase{int32(-150), true, false, ""}, + gtTestCase{int32(-149), true, false, ""}, + gtTestCase{int32(0), true, false, ""}, + gtTestCase{int32(17), true, false, ""}, + + gtTestCase{int64(-(1 << 30)), false, false, ""}, + gtTestCase{int64(-151), false, false, ""}, + gtTestCase{int64(-150), true, false, ""}, + gtTestCase{int64(-149), true, false, ""}, + gtTestCase{int64(0), true, false, ""}, + gtTestCase{int64(17), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint((1 << 32) - 151), true, false, ""}, + gtTestCase{uint(0), true, false, ""}, + gtTestCase{uint(17), true, false, ""}, + + gtTestCase{uint8(0), true, false, ""}, + gtTestCase{uint8(17), true, false, ""}, + gtTestCase{uint8(253), true, false, ""}, + + gtTestCase{uint16((1 << 16) - 151), true, false, ""}, + gtTestCase{uint16(0), true, false, ""}, + gtTestCase{uint16(17), true, false, ""}, + + gtTestCase{uint32((1 << 32) - 151), true, false, ""}, + gtTestCase{uint32(0), true, false, ""}, + gtTestCase{uint32(17), true, false, ""}, + + gtTestCase{uint64((1 << 64) - 151), true, false, ""}, + gtTestCase{uint64(0), true, false, ""}, + gtTestCase{uint64(17), true, false, ""}, + + // Floating point. + gtTestCase{float32(-(1 << 30)), false, false, ""}, + gtTestCase{float32(-151), false, false, ""}, + gtTestCase{float32(-150.2), false, false, ""}, + gtTestCase{float32(-150.1), false, false, ""}, + gtTestCase{float32(-150), true, false, ""}, + gtTestCase{float32(0), true, false, ""}, + gtTestCase{float32(17), true, false, ""}, + gtTestCase{float32(160), true, false, ""}, + + gtTestCase{float64(-(1 << 30)), false, false, ""}, + gtTestCase{float64(-151), false, false, ""}, + gtTestCase{float64(-150.2), false, false, ""}, + gtTestCase{float64(-150.1), false, false, ""}, + gtTestCase{float64(-150), true, false, ""}, + gtTestCase{float64(0), true, false, ""}, + gtTestCase{float64(17), true, false, ""}, + gtTestCase{float64(160), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) PositiveFloatLiteral() { + matcher := GreaterThan(149.9) + desc := matcher.Description() + expectedDesc := "greater than 149.9" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-1, false, false, ""}, + gtTestCase{149, false, false, ""}, + gtTestCase{149.9, false, false, ""}, + gtTestCase{150, true, false, ""}, + gtTestCase{151, true, false, ""}, + + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(149), false, false, ""}, + gtTestCase{int(150), true, false, ""}, + gtTestCase{int(151), true, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(0), false, false, ""}, + gtTestCase{int8(17), false, false, ""}, + gtTestCase{int8(127), false, false, ""}, + + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(149), false, false, ""}, + gtTestCase{int16(150), true, false, ""}, + gtTestCase{int16(151), true, false, ""}, + + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(149), false, false, ""}, + gtTestCase{int32(150), true, false, ""}, + gtTestCase{int32(151), true, false, ""}, + + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(149), false, false, ""}, + gtTestCase{int64(150), true, false, ""}, + gtTestCase{int64(151), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(149), false, false, ""}, + gtTestCase{uint(150), true, false, ""}, + gtTestCase{uint(151), true, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(127), false, false, ""}, + + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(149), false, false, ""}, + gtTestCase{uint16(150), true, false, ""}, + gtTestCase{uint16(151), true, false, ""}, + + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(149), false, false, ""}, + gtTestCase{uint32(150), true, false, ""}, + gtTestCase{uint32(151), true, false, ""}, + + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(149), false, false, ""}, + gtTestCase{uint64(150), true, false, ""}, + gtTestCase{uint64(151), true, false, ""}, + + // Floating point. + gtTestCase{float32(-1), false, false, ""}, + gtTestCase{float32(149), false, false, ""}, + gtTestCase{float32(149.8), false, false, ""}, + gtTestCase{float32(149.9), false, false, ""}, + gtTestCase{float32(150), true, false, ""}, + gtTestCase{float32(151), true, false, ""}, + + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(149), false, false, ""}, + gtTestCase{float64(149.8), false, false, ""}, + gtTestCase{float64(149.9), false, false, ""}, + gtTestCase{float64(150), true, false, ""}, + gtTestCase{float64(151), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Subtle cases +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterThanTest) Int64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := GreaterThan(int64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-1, false, false, ""}, + gtTestCase{kTwoTo25 + 0, false, false, ""}, + gtTestCase{kTwoTo25 + 1, false, false, ""}, + gtTestCase{kTwoTo25 + 2, true, false, ""}, + + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(127), false, false, ""}, + + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(0), false, false, ""}, + gtTestCase{int16(32767), false, false, ""}, + + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int32(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 2), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(255), false, false, ""}, + + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(65535), false, false, ""}, + + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint32(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + + // Floating point. + gtTestCase{float32(-1), false, false, ""}, + gtTestCase{float32(kTwoTo25 - 2), false, false, ""}, + gtTestCase{float32(kTwoTo25 - 1), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 2), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 3), true, false, ""}, + + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(kTwoTo25 - 2), false, false, ""}, + gtTestCase{float64(kTwoTo25 - 1), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 2), true, false, ""}, + gtTestCase{float64(kTwoTo25 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) Int64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := GreaterThan(int64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-1, false, false, ""}, + gtTestCase{1 << 30, false, false, ""}, + + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(math.MaxInt32), false, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(127), false, false, ""}, + + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(0), false, false, ""}, + gtTestCase{int16(32767), false, false, ""}, + + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(math.MaxInt32), false, false, ""}, + + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 2), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(math.MaxUint32), false, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(255), false, false, ""}, + + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(65535), false, false, ""}, + + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(math.MaxUint32), false, false, ""}, + + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + + // Floating point. + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(kTwoTo54 - 2), false, false, ""}, + gtTestCase{float64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 2), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) Uint64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := GreaterThan(uint64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-1, false, false, ""}, + gtTestCase{kTwoTo25 + 0, false, false, ""}, + gtTestCase{kTwoTo25 + 1, false, false, ""}, + gtTestCase{kTwoTo25 + 2, true, false, ""}, + + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(127), false, false, ""}, + + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(0), false, false, ""}, + gtTestCase{int16(32767), false, false, ""}, + + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int32(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 2), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(255), false, false, ""}, + + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(65535), false, false, ""}, + + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint32(kTwoTo25 + 2), true, false, ""}, + + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + + // Floating point. + gtTestCase{float32(-1), false, false, ""}, + gtTestCase{float32(kTwoTo25 - 2), false, false, ""}, + gtTestCase{float32(kTwoTo25 - 1), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 2), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 3), true, false, ""}, + + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(kTwoTo25 - 2), false, false, ""}, + gtTestCase{float64(kTwoTo25 - 1), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 2), true, false, ""}, + gtTestCase{float64(kTwoTo25 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) Uint64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := GreaterThan(uint64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{-1, false, false, ""}, + gtTestCase{1 << 30, false, false, ""}, + + gtTestCase{int(-1), false, false, ""}, + gtTestCase{int(math.MaxInt32), false, false, ""}, + + gtTestCase{int8(-1), false, false, ""}, + gtTestCase{int8(127), false, false, ""}, + + gtTestCase{int16(-1), false, false, ""}, + gtTestCase{int16(0), false, false, ""}, + gtTestCase{int16(32767), false, false, ""}, + + gtTestCase{int32(-1), false, false, ""}, + gtTestCase{int32(math.MaxInt32), false, false, ""}, + + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 2), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint(0), false, false, ""}, + gtTestCase{uint(math.MaxUint32), false, false, ""}, + + gtTestCase{uint8(0), false, false, ""}, + gtTestCase{uint8(255), false, false, ""}, + + gtTestCase{uint16(0), false, false, ""}, + gtTestCase{uint16(65535), false, false, ""}, + + gtTestCase{uint32(0), false, false, ""}, + gtTestCase{uint32(math.MaxUint32), false, false, ""}, + + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + + // Floating point. + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(kTwoTo54 - 2), false, false, ""}, + gtTestCase{float64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 2), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) Float32AboveExactIntegerRange() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := GreaterThan(float32(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than 3.3554432e+07" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(kTwoTo25 - 2), false, false, ""}, + gtTestCase{int64(kTwoTo25 - 1), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 2), false, false, ""}, + gtTestCase{int64(kTwoTo25 + 3), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(kTwoTo25 - 2), false, false, ""}, + gtTestCase{uint64(kTwoTo25 - 1), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + gtTestCase{uint64(kTwoTo25 + 3), true, false, ""}, + + // Floating point. + gtTestCase{float32(-1), false, false, ""}, + gtTestCase{float32(kTwoTo25 - 2), false, false, ""}, + gtTestCase{float32(kTwoTo25 - 1), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 0), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 1), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 2), false, false, ""}, + gtTestCase{float32(kTwoTo25 + 3), true, false, ""}, + + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(kTwoTo25 - 2), false, false, ""}, + gtTestCase{float64(kTwoTo25 - 1), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 0), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 1), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 2), false, false, ""}, + gtTestCase{float64(kTwoTo25 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) Float64AboveExactIntegerRange() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := GreaterThan(float64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "greater than 1.8014398509481984e+16" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + // Signed integers. + gtTestCase{int64(-1), false, false, ""}, + gtTestCase{int64(kTwoTo54 - 2), false, false, ""}, + gtTestCase{int64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 2), false, false, ""}, + gtTestCase{int64(kTwoTo54 + 3), true, false, ""}, + + // Unsigned integers. + gtTestCase{uint64(0), false, false, ""}, + gtTestCase{uint64(kTwoTo54 - 2), false, false, ""}, + gtTestCase{uint64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + gtTestCase{uint64(kTwoTo54 + 3), true, false, ""}, + + // Floating point. + gtTestCase{float64(-1), false, false, ""}, + gtTestCase{float64(kTwoTo54 - 2), false, false, ""}, + gtTestCase{float64(kTwoTo54 - 1), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 0), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 1), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 2), false, false, ""}, + gtTestCase{float64(kTwoTo54 + 3), true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// String literals +//////////////////////////////////////////////////////////////////////// + +func (t *GreaterThanTest) EmptyString() { + matcher := GreaterThan("") + desc := matcher.Description() + expectedDesc := "greater than \"\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + gtTestCase{"", false, false, ""}, + gtTestCase{"\x00", true, false, ""}, + gtTestCase{"a", true, false, ""}, + gtTestCase{"foo", true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) SingleNullByte() { + matcher := GreaterThan("\x00") + desc := matcher.Description() + expectedDesc := "greater than \"\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + gtTestCase{"", false, false, ""}, + gtTestCase{"\x00", false, false, ""}, + gtTestCase{"\x00\x00", true, false, ""}, + gtTestCase{"a", true, false, ""}, + gtTestCase{"foo", true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *GreaterThanTest) LongerString() { + matcher := GreaterThan("foo\x00") + desc := matcher.Description() + expectedDesc := "greater than \"foo\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []gtTestCase{ + gtTestCase{"", false, false, ""}, + gtTestCase{"\x00", false, false, ""}, + gtTestCase{"bar", false, false, ""}, + gtTestCase{"foo", false, false, ""}, + gtTestCase{"foo\x00", false, false, ""}, + gtTestCase{"foo\x00\x00", true, false, ""}, + gtTestCase{"fooa", true, false, ""}, + gtTestCase{"qux", true, false, ""}, + } + + t.checkTestCases(matcher, cases) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as.go new file mode 100644 index 0000000..3b286f7 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as.go @@ -0,0 +1,37 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "fmt" + "reflect" +) + +// HasSameTypeAs returns a matcher that matches values with exactly the same +// type as the supplied prototype. +func HasSameTypeAs(p interface{}) Matcher { + expected := reflect.TypeOf(p) + pred := func(c interface{}) error { + actual := reflect.TypeOf(c) + if actual != expected { + return fmt.Errorf("which has type %v", actual) + } + + return nil + } + + return NewMatcher(pred, fmt.Sprintf("has type %v", expected)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as_test.go new file mode 100644 index 0000000..9d959e2 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_same_type_as_test.go @@ -0,0 +1,181 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "io" + "testing" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +func TestHasSameTypeAs(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Boilerplate +//////////////////////////////////////////////////////////////////////// + +type HasSameTypeAsTest struct { +} + +func init() { RegisterTestSuite(&HasSameTypeAsTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *HasSameTypeAsTest) CandidateIsLiteralNil() { + matcher := HasSameTypeAs(nil) + var err error + + // Description + ExpectEq("has type ", matcher.Description()) + + // Literal nil + err = matcher.Matches(nil) + ExpectEq(nil, err) + + // nil in interface variable + var r io.Reader + err = matcher.Matches(r) + ExpectEq(nil, err) + + // int + err = matcher.Matches(17) + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type int"))) + + // string + err = matcher.Matches("") + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type string"))) + + // nil map + var m map[string]string + err = matcher.Matches(m) + + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type map[string]string"))) + + // Non-nil map + m = make(map[string]string) + err = matcher.Matches(m) + + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type map[string]string"))) +} + +func (t *HasSameTypeAsTest) CandidateIsNilMap() { + var m map[string]string + matcher := HasSameTypeAs(m) + var err error + + // Description + ExpectEq("has type map[string]string", matcher.Description()) + + // nil map + m = nil + err = matcher.Matches(m) + ExpectEq(nil, err) + + // Non-nil map + m = make(map[string]string) + err = matcher.Matches(m) + ExpectEq(nil, err) + + // Literal nil + err = matcher.Matches(nil) + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type "))) + + // int + err = matcher.Matches(17) + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type int"))) + + // string + err = matcher.Matches("") + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type string"))) +} + +func (t *HasSameTypeAsTest) CandidateIsNilInInterfaceVariable() { + var r io.Reader + matcher := HasSameTypeAs(r) + var err error + + // Description + ExpectEq("has type ", matcher.Description()) + + // nil in interface variable + r = nil + err = matcher.Matches(r) + ExpectEq(nil, err) + + // Literal nil + err = matcher.Matches(nil) + ExpectEq(nil, err) + + // int + err = matcher.Matches(17) + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type int"))) +} + +func (t *HasSameTypeAsTest) CandidateIsString() { + matcher := HasSameTypeAs("") + var err error + + // Description + ExpectEq("has type string", matcher.Description()) + + // string + err = matcher.Matches("taco") + ExpectEq(nil, err) + + // string alias + type Foo string + err = matcher.Matches(Foo("taco")) + ExpectThat(err, Error(MatchesRegexp("which has type .*Foo"))) + + // Literal nil + err = matcher.Matches(nil) + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type "))) + + // int + err = matcher.Matches(17) + AssertNe(nil, err) + ExpectThat(err, Error(Equals("which has type int"))) +} + +func (t *HasSameTypeAsTest) CandidateIsStringAlias() { + type Foo string + matcher := HasSameTypeAs(Foo("")) + var err error + + // Description + ExpectThat(matcher.Description(), MatchesRegexp("has type .*Foo")) + + // string alias + err = matcher.Matches(Foo("taco")) + ExpectEq(nil, err) + + // string + err = matcher.Matches("taco") + ExpectThat(err, Error(Equals("which has type string"))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr.go new file mode 100644 index 0000000..bf5bd6a --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr.go @@ -0,0 +1,46 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +// HasSubstr returns a matcher that matches strings containing s as a +// substring. +func HasSubstr(s string) Matcher { + return NewMatcher( + func(c interface{}) error { return hasSubstr(s, c) }, + fmt.Sprintf("has substring \"%s\"", s)) +} + +func hasSubstr(needle string, c interface{}) error { + v := reflect.ValueOf(c) + if v.Kind() != reflect.String { + return NewFatalError("which is not a string") + } + + // Perform the substring search. + haystack := v.String() + if strings.Contains(haystack, needle) { + return nil + } + + return errors.New("") +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr_test.go new file mode 100644 index 0000000..8ba2220 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/has_substr_test.go @@ -0,0 +1,92 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type HasSubstrTest struct { +} + +func init() { RegisterTestSuite(&HasSubstrTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *HasSubstrTest) Description() { + matcher := HasSubstr("taco") + ExpectThat(matcher.Description(), Equals("has substring \"taco\"")) +} + +func (t *HasSubstrTest) CandidateIsNil() { + matcher := HasSubstr("") + err := matcher.Matches(nil) + + ExpectThat(err, Error(Equals("which is not a string"))) + ExpectTrue(isFatal(err)) +} + +func (t *HasSubstrTest) CandidateIsInteger() { + matcher := HasSubstr("") + err := matcher.Matches(17) + + ExpectThat(err, Error(Equals("which is not a string"))) + ExpectTrue(isFatal(err)) +} + +func (t *HasSubstrTest) CandidateIsByteSlice() { + matcher := HasSubstr("") + err := matcher.Matches([]byte{17}) + + ExpectThat(err, Error(Equals("which is not a string"))) + ExpectTrue(isFatal(err)) +} + +func (t *HasSubstrTest) CandidateDoesntHaveSubstring() { + matcher := HasSubstr("taco") + err := matcher.Matches("tac") + + ExpectThat(err, Error(Equals(""))) + ExpectFalse(isFatal(err)) +} + +func (t *HasSubstrTest) CandidateEqualsArg() { + matcher := HasSubstr("taco") + err := matcher.Matches("taco") + + ExpectThat(err, Equals(nil)) +} + +func (t *HasSubstrTest) CandidateHasProperSubstring() { + matcher := HasSubstr("taco") + err := matcher.Matches("burritos and tacos") + + ExpectThat(err, Equals(nil)) +} + +func (t *HasSubstrTest) EmptyStringIsAlwaysSubString() { + matcher := HasSubstr("") + err := matcher.Matches("asdf") + + ExpectThat(err, Equals(nil)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to.go new file mode 100644 index 0000000..ae6460e --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to.go @@ -0,0 +1,134 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" +) + +// Is the type comparable according to the definition here? +// +// http://weekly.golang.org/doc/go_spec.html#Comparison_operators +// +func isComparable(t reflect.Type) bool { + switch t.Kind() { + case reflect.Array: + return isComparable(t.Elem()) + + case reflect.Struct: + for i := 0; i < t.NumField(); i++ { + if !isComparable(t.Field(i).Type) { + return false + } + } + + return true + + case reflect.Slice, reflect.Map, reflect.Func: + return false + } + + return true +} + +// Should the supplied type be allowed as an argument to IdenticalTo? +func isLegalForIdenticalTo(t reflect.Type) (bool, error) { + // Allow the zero type. + if t == nil { + return true, nil + } + + // Reference types are always okay; we compare pointers. + switch t.Kind() { + case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan: + return true, nil + } + + // Reject other non-comparable types. + if !isComparable(t) { + return false, errors.New(fmt.Sprintf("%v is not comparable", t)) + } + + return true, nil +} + +// IdenticalTo(x) returns a matcher that matches values v with type identical +// to x such that: +// +// 1. If v and x are of a reference type (slice, map, function, channel), then +// they are either both nil or are references to the same object. +// +// 2. Otherwise, if v and x are not of a reference type but have a valid type, +// then v == x. +// +// If v and x are both the invalid type (which results from the predeclared nil +// value, or from nil interface variables), then the matcher is satisfied. +// +// This function will panic if x is of a value type that is not comparable. For +// example, x cannot be an array of functions. +func IdenticalTo(x interface{}) Matcher { + t := reflect.TypeOf(x) + + // Reject illegal arguments. + if ok, err := isLegalForIdenticalTo(t); !ok { + panic("IdenticalTo: " + err.Error()) + } + + return &identicalToMatcher{x} +} + +type identicalToMatcher struct { + x interface{} +} + +func (m *identicalToMatcher) Description() string { + t := reflect.TypeOf(m.x) + return fmt.Sprintf("identical to <%v> %v", t, m.x) +} + +func (m *identicalToMatcher) Matches(c interface{}) error { + // Make sure the candidate's type is correct. + t := reflect.TypeOf(m.x) + if ct := reflect.TypeOf(c); t != ct { + return NewFatalError(fmt.Sprintf("which is of type %v", ct)) + } + + // Special case: two values of the invalid type are always identical. + if t == nil { + return nil + } + + // Handle reference types. + switch t.Kind() { + case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan: + xv := reflect.ValueOf(m.x) + cv := reflect.ValueOf(c) + if xv.Pointer() == cv.Pointer() { + return nil + } + + return errors.New("which is not an identical reference") + } + + // Are the values equal? + if m.x == c { + return nil + } + + return errors.New("") +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to_test.go new file mode 100644 index 0000000..047754c --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/identical_to_test.go @@ -0,0 +1,849 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "fmt" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "io" + "unsafe" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type IdenticalToTest struct { +} + +func init() { RegisterTestSuite(&IdenticalToTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *IdenticalToTest) TypesNotIdentical() { + var m Matcher + var err error + + type intAlias int + + // Type alias expected value + m = IdenticalTo(intAlias(17)) + err = m.Matches(int(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int"))) + + // Type alias candidate + m = IdenticalTo(int(17)) + err = m.Matches(intAlias(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.intAlias"))) + + // int and uint + m = IdenticalTo(int(17)) + err = m.Matches(uint(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type uint"))) +} + +func (t *IdenticalToTest) PredeclaredNilIdentifier() { + var m Matcher + var err error + + // Nil literal + m = IdenticalTo(nil) + err = m.Matches(nil) + ExpectEq(nil, err) + + // Zero interface var (which is the same as above since IdenticalTo takes an + // interface{} as an arg) + var nilReader io.Reader + var nilWriter io.Writer + + m = IdenticalTo(nilReader) + err = m.Matches(nilWriter) + ExpectEq(nil, err) + + // Typed nil value. + m = IdenticalTo(nil) + err = m.Matches((chan int)(nil)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type chan int"))) + + // Non-nil value. + m = IdenticalTo(nil) + err = m.Matches("taco") + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type string"))) +} + +func (t *IdenticalToTest) Slices() { + var m Matcher + var err error + + // Nil expected value + m = IdenticalTo(([]int)(nil)) + ExpectEq("identical to <[]int> []", m.Description()) + + err = m.Matches(([]int)(nil)) + ExpectEq(nil, err) + + err = m.Matches([]int{}) + ExpectThat(err, Error(Equals("which is not an identical reference"))) + + // Non-nil expected value + o1 := make([]int, 1) + o2 := make([]int, 1) + m = IdenticalTo(o1) + ExpectEq(fmt.Sprintf("identical to <[]int> %v", o1), m.Description()) + + err = m.Matches(o1) + ExpectEq(nil, err) + + err = m.Matches(o2) + ExpectThat(err, Error(Equals("which is not an identical reference"))) +} + +func (t *IdenticalToTest) Maps() { + var m Matcher + var err error + + // Nil expected value + m = IdenticalTo((map[int]int)(nil)) + ExpectEq("identical to map[]", m.Description()) + + err = m.Matches((map[int]int)(nil)) + ExpectEq(nil, err) + + err = m.Matches(map[int]int{}) + ExpectThat(err, Error(Equals("which is not an identical reference"))) + + // Non-nil expected value + o1 := map[int]int{} + o2 := map[int]int{} + m = IdenticalTo(o1) + ExpectEq(fmt.Sprintf("identical to %v", o1), m.Description()) + + err = m.Matches(o1) + ExpectEq(nil, err) + + err = m.Matches(o2) + ExpectThat(err, Error(Equals("which is not an identical reference"))) +} + +func (t *IdenticalToTest) Functions() { + var m Matcher + var err error + + // Nil expected value + m = IdenticalTo((func())(nil)) + ExpectEq("identical to ", m.Description()) + + err = m.Matches((func())(nil)) + ExpectEq(nil, err) + + err = m.Matches(func() {}) + ExpectThat(err, Error(Equals("which is not an identical reference"))) + + // Non-nil expected value + o1 := func() {} + o2 := func() {} + m = IdenticalTo(o1) + ExpectEq(fmt.Sprintf("identical to %v", o1), m.Description()) + + err = m.Matches(o1) + ExpectEq(nil, err) + + err = m.Matches(o2) + ExpectThat(err, Error(Equals("which is not an identical reference"))) +} + +func (t *IdenticalToTest) Channels() { + var m Matcher + var err error + + // Nil expected value + m = IdenticalTo((chan int)(nil)) + ExpectEq("identical to ", m.Description()) + + err = m.Matches((chan int)(nil)) + ExpectEq(nil, err) + + err = m.Matches(make(chan int)) + ExpectThat(err, Error(Equals("which is not an identical reference"))) + + // Non-nil expected value + o1 := make(chan int) + o2 := make(chan int) + m = IdenticalTo(o1) + ExpectEq(fmt.Sprintf("identical to %v", o1), m.Description()) + + err = m.Matches(o1) + ExpectEq(nil, err) + + err = m.Matches(o2) + ExpectThat(err, Error(Equals("which is not an identical reference"))) +} + +func (t *IdenticalToTest) Bools() { + var m Matcher + var err error + + // false + m = IdenticalTo(false) + ExpectEq("identical to false", m.Description()) + + err = m.Matches(false) + ExpectEq(nil, err) + + err = m.Matches(true) + ExpectThat(err, Error(Equals(""))) + + // true + m = IdenticalTo(true) + ExpectEq("identical to true", m.Description()) + + err = m.Matches(false) + ExpectThat(err, Error(Equals(""))) + + err = m.Matches(true) + ExpectEq(nil, err) +} + +func (t *IdenticalToTest) Ints() { + var m Matcher + var err error + + m = IdenticalTo(int(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(int(17)) + ExpectEq(nil, err) + + // Type alias + type myType int + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Int8s() { + var m Matcher + var err error + + m = IdenticalTo(int8(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(int8(17)) + ExpectEq(nil, err) + + // Type alias + type myType int8 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Int16s() { + var m Matcher + var err error + + m = IdenticalTo(int16(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(int16(17)) + ExpectEq(nil, err) + + // Type alias + type myType int16 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Int32s() { + var m Matcher + var err error + + m = IdenticalTo(int32(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(int32(17)) + ExpectEq(nil, err) + + // Type alias + type myType int32 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int16(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int16"))) +} + +func (t *IdenticalToTest) Int64s() { + var m Matcher + var err error + + m = IdenticalTo(int64(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(int64(17)) + ExpectEq(nil, err) + + // Type alias + type myType int64 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Uints() { + var m Matcher + var err error + + m = IdenticalTo(uint(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(uint(17)) + ExpectEq(nil, err) + + // Type alias + type myType uint + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Uint8s() { + var m Matcher + var err error + + m = IdenticalTo(uint8(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(uint8(17)) + ExpectEq(nil, err) + + // Type alias + type myType uint8 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Uint16s() { + var m Matcher + var err error + + m = IdenticalTo(uint16(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(uint16(17)) + ExpectEq(nil, err) + + // Type alias + type myType uint16 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Uint32s() { + var m Matcher + var err error + + m = IdenticalTo(uint32(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(uint32(17)) + ExpectEq(nil, err) + + // Type alias + type myType uint32 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Uint64s() { + var m Matcher + var err error + + m = IdenticalTo(uint64(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(uint64(17)) + ExpectEq(nil, err) + + // Type alias + type myType uint64 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Uintptrs() { + var m Matcher + var err error + + m = IdenticalTo(uintptr(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(uintptr(17)) + ExpectEq(nil, err) + + // Type alias + type myType uintptr + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Float32s() { + var m Matcher + var err error + + m = IdenticalTo(float32(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(float32(17)) + ExpectEq(nil, err) + + // Type alias + type myType float32 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Float64s() { + var m Matcher + var err error + + m = IdenticalTo(float64(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(float64(17)) + ExpectEq(nil, err) + + // Type alias + type myType float64 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Complex64s() { + var m Matcher + var err error + + m = IdenticalTo(complex64(17)) + ExpectEq("identical to (17+0i)", m.Description()) + + // Identical value + err = m.Matches(complex64(17)) + ExpectEq(nil, err) + + // Type alias + type myType complex64 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) Complex128s() { + var m Matcher + var err error + + m = IdenticalTo(complex128(17)) + ExpectEq("identical to (17+0i)", m.Description()) + + // Identical value + err = m.Matches(complex128(17)) + ExpectEq(nil, err) + + // Type alias + type myType complex128 + err = m.Matches(myType(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) EmptyComparableArrays() { + var m Matcher + var err error + + m = IdenticalTo([0]int{}) + ExpectEq("identical to <[0]int> []", m.Description()) + + // Identical value + err = m.Matches([0]int{}) + ExpectEq(nil, err) + + // Length too long + err = m.Matches([1]int{17}) + ExpectThat(err, Error(Equals("which is of type [1]int"))) + + // Element type alias + type myType int + err = m.Matches([0]myType{}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type [0]oglematchers_test.myType"))) + + // Completely wrong element type + err = m.Matches([0]int32{}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type [0]int32"))) +} + +func (t *IdenticalToTest) NonEmptyComparableArrays() { + var m Matcher + var err error + + m = IdenticalTo([2]int{17, 19}) + ExpectEq("identical to <[2]int> [17 19]", m.Description()) + + // Identical value + err = m.Matches([2]int{17, 19}) + ExpectEq(nil, err) + + // Length too short + err = m.Matches([1]int{17}) + ExpectThat(err, Error(Equals("which is of type [1]int"))) + + // Length too long + err = m.Matches([3]int{17, 19, 23}) + ExpectThat(err, Error(Equals("which is of type [3]int"))) + + // First element different + err = m.Matches([2]int{13, 19}) + ExpectThat(err, Error(Equals(""))) + + // Second element different + err = m.Matches([2]int{17, 23}) + ExpectThat(err, Error(Equals(""))) + + // Element type alias + type myType int + err = m.Matches([2]myType{17, 19}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type [2]oglematchers_test.myType"))) + + // Completely wrong element type + err = m.Matches([2]int32{17, 19}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type [2]int32"))) +} + +func (t *IdenticalToTest) NonEmptyArraysOfComparableArrays() { + var m Matcher + var err error + + x := [2][2]int{ + [2]int{17, 19}, + [2]int{23, 29}, + } + m = IdenticalTo(x) + ExpectEq("identical to <[2][2]int> [[17 19] [23 29]]", m.Description()) + + // Identical value + err = m.Matches([2][2]int{[2]int{17, 19}, [2]int{23, 29}}) + ExpectEq(nil, err) + + // Outer length too short + err = m.Matches([1][2]int{[2]int{17, 19}}) + ExpectThat(err, Error(Equals("which is of type [1][2]int"))) + + // Inner length too short + err = m.Matches([2][1]int{[1]int{17}, [1]int{23}}) + ExpectThat(err, Error(Equals("which is of type [2][1]int"))) + + // First element different + err = m.Matches([2][2]int{[2]int{13, 19}, [2]int{23, 29}}) + ExpectThat(err, Error(Equals(""))) + + // Element type alias + type myType int + err = m.Matches([2][2]myType{[2]myType{17, 19}, [2]myType{23, 29}}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type [2][2]oglematchers_test.myType"))) +} + +func (t *IdenticalToTest) NonComparableArrays() { + x := [0]func(){} + f := func() { IdenticalTo(x) } + ExpectThat(f, Panics(HasSubstr("is not comparable"))) +} + +func (t *IdenticalToTest) ArraysOfNonComparableArrays() { + x := [0][0]func(){} + f := func() { IdenticalTo(x) } + ExpectThat(f, Panics(HasSubstr("is not comparable"))) +} + +func (t *IdenticalToTest) Strings() { + var m Matcher + var err error + + m = IdenticalTo("taco") + ExpectEq("identical to taco", m.Description()) + + // Identical value + err = m.Matches("ta" + "co") + ExpectEq(nil, err) + + // Type alias + type myType string + err = m.Matches(myType("taco")) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) ComparableStructs() { + var m Matcher + var err error + + type subStruct struct { + i int + } + + type myStruct struct { + u uint + s subStruct + } + + x := myStruct{17, subStruct{19}} + m = IdenticalTo(x) + ExpectEq("identical to {17 {19}}", m.Description()) + + // Identical value + err = m.Matches(myStruct{17, subStruct{19}}) + ExpectEq(nil, err) + + // Wrong outer field + err = m.Matches(myStruct{13, subStruct{19}}) + ExpectThat(err, Error(Equals(""))) + + // Wrong inner field + err = m.Matches(myStruct{17, subStruct{23}}) + ExpectThat(err, Error(Equals(""))) + + // Type alias + type myType myStruct + err = m.Matches(myType{17, subStruct{19}}) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) NonComparableStructs() { + type subStruct struct { + s []int + } + + type myStruct struct { + u uint + s subStruct + } + + x := myStruct{17, subStruct{[]int{19}}} + f := func() { IdenticalTo(x) } + ExpectThat(f, Panics(AllOf(HasSubstr("IdenticalTo"), HasSubstr("comparable")))) +} + +func (t *IdenticalToTest) NilUnsafePointer() { + var m Matcher + var err error + + x := unsafe.Pointer(nil) + m = IdenticalTo(x) + ExpectEq(fmt.Sprintf("identical to %v", x), m.Description()) + + // Identical value + err = m.Matches(unsafe.Pointer(nil)) + ExpectEq(nil, err) + + // Wrong value + j := 17 + err = m.Matches(unsafe.Pointer(&j)) + ExpectThat(err, Error(Equals(""))) + + // Type alias + type myType unsafe.Pointer + err = m.Matches(myType(unsafe.Pointer(nil))) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) NonNilUnsafePointer() { + var m Matcher + var err error + + i := 17 + x := unsafe.Pointer(&i) + m = IdenticalTo(x) + ExpectEq(fmt.Sprintf("identical to %v", x), m.Description()) + + // Identical value + err = m.Matches(unsafe.Pointer(&i)) + ExpectEq(nil, err) + + // Nil value + err = m.Matches(unsafe.Pointer(nil)) + ExpectThat(err, Error(Equals(""))) + + // Wrong value + j := 17 + err = m.Matches(unsafe.Pointer(&j)) + ExpectThat(err, Error(Equals(""))) + + // Type alias + type myType unsafe.Pointer + err = m.Matches(myType(unsafe.Pointer(&i))) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type oglematchers_test.myType"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} + +func (t *IdenticalToTest) IntAlias() { + var m Matcher + var err error + + type intAlias int + + m = IdenticalTo(intAlias(17)) + ExpectEq("identical to 17", m.Description()) + + // Identical value + err = m.Matches(intAlias(17)) + ExpectEq(nil, err) + + // Int + err = m.Matches(int(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int"))) + + // Completely wrong type + err = m.Matches(int32(17)) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("which is of type int32"))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go new file mode 100644 index 0000000..8402cde --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go @@ -0,0 +1,41 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "fmt" + "reflect" +) + +// LessOrEqual returns a matcher that matches integer, floating point, or +// strings values v such that v <= x. Comparison is not defined between numeric +// and string types, but is defined between all integer and floating point +// types. +// +// x must itself be an integer, floating point, or string type; otherwise, +// LessOrEqual will panic. +func LessOrEqual(x interface{}) Matcher { + desc := fmt.Sprintf("less than or equal to %v", x) + + // Special case: make it clear that strings are strings. + if reflect.TypeOf(x).Kind() == reflect.String { + desc = fmt.Sprintf("less than or equal to \"%s\"", x) + } + + // Put LessThan last so that its error messages will be used in the event of + // failure. + return transformDescription(AnyOf(Equals(x), LessThan(x)), desc) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal_test.go new file mode 100644 index 0000000..100e230 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal_test.go @@ -0,0 +1,1077 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "math" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type LessOrEqualTest struct { +} + +func init() { RegisterTestSuite(&LessOrEqualTest{}) } + +type leTestCase struct { + candidate interface{} + expectedResult bool + shouldBeFatal bool + expectedError string +} + +func (t *LessOrEqualTest) checkTestCases(matcher Matcher, cases []leTestCase) { + for i, c := range cases { + err := matcher.Matches(c.candidate) + + ExpectThat( + (err == nil), + Equals(c.expectedResult), + "Case %d (candidate %v)", + i, + c.candidate) + + if err == nil { + continue + } + + _, isFatal := err.(*FatalError) + ExpectEq( + c.shouldBeFatal, + isFatal, + "Case %d (candidate %v)", + i, + c.candidate) + + ExpectThat( + err, + Error(Equals(c.expectedError)), + "Case %d (candidate %v)", + i, + c.candidate) + } +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessOrEqualTest) IntegerCandidateBadTypes() { + matcher := LessOrEqual(int(-150)) + + cases := []leTestCase{ + leTestCase{true, false, true, "which is not comparable"}, + leTestCase{complex64(-151), false, true, "which is not comparable"}, + leTestCase{complex128(-151), false, true, "which is not comparable"}, + leTestCase{[...]int{-151}, false, true, "which is not comparable"}, + leTestCase{make(chan int), false, true, "which is not comparable"}, + leTestCase{func() {}, false, true, "which is not comparable"}, + leTestCase{map[int]int{}, false, true, "which is not comparable"}, + leTestCase{&leTestCase{}, false, true, "which is not comparable"}, + leTestCase{make([]int, 0), false, true, "which is not comparable"}, + leTestCase{"-151", false, true, "which is not comparable"}, + leTestCase{leTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) FloatCandidateBadTypes() { + matcher := LessOrEqual(float32(-150)) + + cases := []leTestCase{ + leTestCase{true, false, true, "which is not comparable"}, + leTestCase{complex64(-151), false, true, "which is not comparable"}, + leTestCase{complex128(-151), false, true, "which is not comparable"}, + leTestCase{[...]int{-151}, false, true, "which is not comparable"}, + leTestCase{make(chan int), false, true, "which is not comparable"}, + leTestCase{func() {}, false, true, "which is not comparable"}, + leTestCase{map[int]int{}, false, true, "which is not comparable"}, + leTestCase{&leTestCase{}, false, true, "which is not comparable"}, + leTestCase{make([]int, 0), false, true, "which is not comparable"}, + leTestCase{"-151", false, true, "which is not comparable"}, + leTestCase{leTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) StringCandidateBadTypes() { + matcher := LessOrEqual("17") + + cases := []leTestCase{ + leTestCase{true, false, true, "which is not comparable"}, + leTestCase{int(0), false, true, "which is not comparable"}, + leTestCase{int8(0), false, true, "which is not comparable"}, + leTestCase{int16(0), false, true, "which is not comparable"}, + leTestCase{int32(0), false, true, "which is not comparable"}, + leTestCase{int64(0), false, true, "which is not comparable"}, + leTestCase{uint(0), false, true, "which is not comparable"}, + leTestCase{uint8(0), false, true, "which is not comparable"}, + leTestCase{uint16(0), false, true, "which is not comparable"}, + leTestCase{uint32(0), false, true, "which is not comparable"}, + leTestCase{uint64(0), false, true, "which is not comparable"}, + leTestCase{float32(0), false, true, "which is not comparable"}, + leTestCase{float64(0), false, true, "which is not comparable"}, + leTestCase{complex64(-151), false, true, "which is not comparable"}, + leTestCase{complex128(-151), false, true, "which is not comparable"}, + leTestCase{[...]int{-151}, false, true, "which is not comparable"}, + leTestCase{make(chan int), false, true, "which is not comparable"}, + leTestCase{func() {}, false, true, "which is not comparable"}, + leTestCase{map[int]int{}, false, true, "which is not comparable"}, + leTestCase{&leTestCase{}, false, true, "which is not comparable"}, + leTestCase{make([]int, 0), false, true, "which is not comparable"}, + leTestCase{leTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) BadArgument() { + panicked := false + + defer func() { + ExpectThat(panicked, Equals(true)) + }() + + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + + LessOrEqual(complex128(0)) +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessOrEqualTest) NegativeIntegerLiteral() { + matcher := LessOrEqual(-150) + desc := matcher.Description() + expectedDesc := "less than or equal to -150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-(1 << 30), true, false, ""}, + leTestCase{-151, true, false, ""}, + leTestCase{-150, true, false, ""}, + leTestCase{-149, false, false, ""}, + leTestCase{0, false, false, ""}, + leTestCase{17, false, false, ""}, + + leTestCase{int(-(1 << 30)), true, false, ""}, + leTestCase{int(-151), true, false, ""}, + leTestCase{int(-150), true, false, ""}, + leTestCase{int(-149), false, false, ""}, + leTestCase{int(0), false, false, ""}, + leTestCase{int(17), false, false, ""}, + + leTestCase{int8(-127), false, false, ""}, + leTestCase{int8(0), false, false, ""}, + leTestCase{int8(17), false, false, ""}, + + leTestCase{int16(-(1 << 14)), true, false, ""}, + leTestCase{int16(-151), true, false, ""}, + leTestCase{int16(-150), true, false, ""}, + leTestCase{int16(-149), false, false, ""}, + leTestCase{int16(0), false, false, ""}, + leTestCase{int16(17), false, false, ""}, + + leTestCase{int32(-(1 << 30)), true, false, ""}, + leTestCase{int32(-151), true, false, ""}, + leTestCase{int32(-150), true, false, ""}, + leTestCase{int32(-149), false, false, ""}, + leTestCase{int32(0), false, false, ""}, + leTestCase{int32(17), false, false, ""}, + + leTestCase{int64(-(1 << 30)), true, false, ""}, + leTestCase{int64(-151), true, false, ""}, + leTestCase{int64(-150), true, false, ""}, + leTestCase{int64(-149), false, false, ""}, + leTestCase{int64(0), false, false, ""}, + leTestCase{int64(17), false, false, ""}, + + // Unsigned integers. + leTestCase{uint((1 << 32) - 151), false, false, ""}, + leTestCase{uint(0), false, false, ""}, + leTestCase{uint(17), false, false, ""}, + + leTestCase{uint8(0), false, false, ""}, + leTestCase{uint8(17), false, false, ""}, + leTestCase{uint8(253), false, false, ""}, + + leTestCase{uint16((1 << 16) - 151), false, false, ""}, + leTestCase{uint16(0), false, false, ""}, + leTestCase{uint16(17), false, false, ""}, + + leTestCase{uint32((1 << 32) - 151), false, false, ""}, + leTestCase{uint32(0), false, false, ""}, + leTestCase{uint32(17), false, false, ""}, + + leTestCase{uint64((1 << 64) - 151), false, false, ""}, + leTestCase{uint64(0), false, false, ""}, + leTestCase{uint64(17), false, false, ""}, + + // Floating point. + leTestCase{float32(-(1 << 30)), true, false, ""}, + leTestCase{float32(-151), true, false, ""}, + leTestCase{float32(-150.1), true, false, ""}, + leTestCase{float32(-150), true, false, ""}, + leTestCase{float32(-149.9), false, false, ""}, + leTestCase{float32(0), false, false, ""}, + leTestCase{float32(17), false, false, ""}, + leTestCase{float32(160), false, false, ""}, + + leTestCase{float64(-(1 << 30)), true, false, ""}, + leTestCase{float64(-151), true, false, ""}, + leTestCase{float64(-150.1), true, false, ""}, + leTestCase{float64(-150), true, false, ""}, + leTestCase{float64(-149.9), false, false, ""}, + leTestCase{float64(0), false, false, ""}, + leTestCase{float64(17), false, false, ""}, + leTestCase{float64(160), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) ZeroIntegerLiteral() { + matcher := LessOrEqual(0) + desc := matcher.Description() + expectedDesc := "less than or equal to 0" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-(1 << 30), true, false, ""}, + leTestCase{-1, true, false, ""}, + leTestCase{0, true, false, ""}, + leTestCase{1, false, false, ""}, + leTestCase{17, false, false, ""}, + leTestCase{(1 << 30), false, false, ""}, + + leTestCase{int(-(1 << 30)), true, false, ""}, + leTestCase{int(-1), true, false, ""}, + leTestCase{int(0), true, false, ""}, + leTestCase{int(1), false, false, ""}, + leTestCase{int(17), false, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(0), true, false, ""}, + leTestCase{int8(1), false, false, ""}, + + leTestCase{int16(-(1 << 14)), true, false, ""}, + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(0), true, false, ""}, + leTestCase{int16(1), false, false, ""}, + leTestCase{int16(17), false, false, ""}, + + leTestCase{int32(-(1 << 30)), true, false, ""}, + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(0), true, false, ""}, + leTestCase{int32(1), false, false, ""}, + leTestCase{int32(17), false, false, ""}, + + leTestCase{int64(-(1 << 30)), true, false, ""}, + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(0), true, false, ""}, + leTestCase{int64(1), false, false, ""}, + leTestCase{int64(17), false, false, ""}, + + // Unsigned integers. + leTestCase{uint((1 << 32) - 1), false, false, ""}, + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(1), false, false, ""}, + leTestCase{uint(17), false, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(1), false, false, ""}, + leTestCase{uint8(17), false, false, ""}, + leTestCase{uint8(253), false, false, ""}, + + leTestCase{uint16((1 << 16) - 1), false, false, ""}, + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(1), false, false, ""}, + leTestCase{uint16(17), false, false, ""}, + + leTestCase{uint32((1 << 32) - 1), false, false, ""}, + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(1), false, false, ""}, + leTestCase{uint32(17), false, false, ""}, + + leTestCase{uint64((1 << 64) - 1), false, false, ""}, + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(1), false, false, ""}, + leTestCase{uint64(17), false, false, ""}, + + // Floating point. + leTestCase{float32(-(1 << 30)), true, false, ""}, + leTestCase{float32(-1), true, false, ""}, + leTestCase{float32(-0.1), true, false, ""}, + leTestCase{float32(-0.0), true, false, ""}, + leTestCase{float32(0), true, false, ""}, + leTestCase{float32(0.1), false, false, ""}, + leTestCase{float32(17), false, false, ""}, + leTestCase{float32(160), false, false, ""}, + + leTestCase{float64(-(1 << 30)), true, false, ""}, + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(-0.1), true, false, ""}, + leTestCase{float64(-0), true, false, ""}, + leTestCase{float64(0), true, false, ""}, + leTestCase{float64(0.1), false, false, ""}, + leTestCase{float64(17), false, false, ""}, + leTestCase{float64(160), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) PositiveIntegerLiteral() { + matcher := LessOrEqual(150) + desc := matcher.Description() + expectedDesc := "less than or equal to 150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-1, true, false, ""}, + leTestCase{149, true, false, ""}, + leTestCase{150, true, false, ""}, + leTestCase{151, false, false, ""}, + + leTestCase{int(-1), true, false, ""}, + leTestCase{int(149), true, false, ""}, + leTestCase{int(150), true, false, ""}, + leTestCase{int(151), false, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(0), true, false, ""}, + leTestCase{int8(17), true, false, ""}, + leTestCase{int8(127), true, false, ""}, + + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(149), true, false, ""}, + leTestCase{int16(150), true, false, ""}, + leTestCase{int16(151), false, false, ""}, + + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(149), true, false, ""}, + leTestCase{int32(150), true, false, ""}, + leTestCase{int32(151), false, false, ""}, + + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(149), true, false, ""}, + leTestCase{int64(150), true, false, ""}, + leTestCase{int64(151), false, false, ""}, + + // Unsigned integers. + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(149), true, false, ""}, + leTestCase{uint(150), true, false, ""}, + leTestCase{uint(151), false, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(127), true, false, ""}, + + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(149), true, false, ""}, + leTestCase{uint16(150), true, false, ""}, + leTestCase{uint16(151), false, false, ""}, + + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(149), true, false, ""}, + leTestCase{uint32(150), true, false, ""}, + leTestCase{uint32(151), false, false, ""}, + + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(149), true, false, ""}, + leTestCase{uint64(150), true, false, ""}, + leTestCase{uint64(151), false, false, ""}, + + // Floating point. + leTestCase{float32(-1), true, false, ""}, + leTestCase{float32(149), true, false, ""}, + leTestCase{float32(149.9), true, false, ""}, + leTestCase{float32(150), true, false, ""}, + leTestCase{float32(150.1), false, false, ""}, + leTestCase{float32(151), false, false, ""}, + + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(149), true, false, ""}, + leTestCase{float64(149.9), true, false, ""}, + leTestCase{float64(150), true, false, ""}, + leTestCase{float64(150.1), false, false, ""}, + leTestCase{float64(151), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Float literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessOrEqualTest) NegativeFloatLiteral() { + matcher := LessOrEqual(-150.1) + desc := matcher.Description() + expectedDesc := "less than or equal to -150.1" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-(1 << 30), true, false, ""}, + leTestCase{-151, true, false, ""}, + leTestCase{-150.1, true, false, ""}, + leTestCase{-150, false, false, ""}, + leTestCase{-149, false, false, ""}, + leTestCase{0, false, false, ""}, + leTestCase{17, false, false, ""}, + + leTestCase{int(-(1 << 30)), true, false, ""}, + leTestCase{int(-151), true, false, ""}, + leTestCase{int(-150), false, false, ""}, + leTestCase{int(-149), false, false, ""}, + leTestCase{int(0), false, false, ""}, + leTestCase{int(17), false, false, ""}, + + leTestCase{int8(-127), false, false, ""}, + leTestCase{int8(0), false, false, ""}, + leTestCase{int8(17), false, false, ""}, + + leTestCase{int16(-(1 << 14)), true, false, ""}, + leTestCase{int16(-151), true, false, ""}, + leTestCase{int16(-150), false, false, ""}, + leTestCase{int16(-149), false, false, ""}, + leTestCase{int16(0), false, false, ""}, + leTestCase{int16(17), false, false, ""}, + + leTestCase{int32(-(1 << 30)), true, false, ""}, + leTestCase{int32(-151), true, false, ""}, + leTestCase{int32(-150), false, false, ""}, + leTestCase{int32(-149), false, false, ""}, + leTestCase{int32(0), false, false, ""}, + leTestCase{int32(17), false, false, ""}, + + leTestCase{int64(-(1 << 30)), true, false, ""}, + leTestCase{int64(-151), true, false, ""}, + leTestCase{int64(-150), false, false, ""}, + leTestCase{int64(-149), false, false, ""}, + leTestCase{int64(0), false, false, ""}, + leTestCase{int64(17), false, false, ""}, + + // Unsigned integers. + leTestCase{uint((1 << 32) - 151), false, false, ""}, + leTestCase{uint(0), false, false, ""}, + leTestCase{uint(17), false, false, ""}, + + leTestCase{uint8(0), false, false, ""}, + leTestCase{uint8(17), false, false, ""}, + leTestCase{uint8(253), false, false, ""}, + + leTestCase{uint16((1 << 16) - 151), false, false, ""}, + leTestCase{uint16(0), false, false, ""}, + leTestCase{uint16(17), false, false, ""}, + + leTestCase{uint32((1 << 32) - 151), false, false, ""}, + leTestCase{uint32(0), false, false, ""}, + leTestCase{uint32(17), false, false, ""}, + + leTestCase{uint64((1 << 64) - 151), false, false, ""}, + leTestCase{uint64(0), false, false, ""}, + leTestCase{uint64(17), false, false, ""}, + + // Floating point. + leTestCase{float32(-(1 << 30)), true, false, ""}, + leTestCase{float32(-151), true, false, ""}, + leTestCase{float32(-150.2), true, false, ""}, + leTestCase{float32(-150.1), true, false, ""}, + leTestCase{float32(-150), false, false, ""}, + leTestCase{float32(0), false, false, ""}, + leTestCase{float32(17), false, false, ""}, + leTestCase{float32(160), false, false, ""}, + + leTestCase{float64(-(1 << 30)), true, false, ""}, + leTestCase{float64(-151), true, false, ""}, + leTestCase{float64(-150.2), true, false, ""}, + leTestCase{float64(-150.1), true, false, ""}, + leTestCase{float64(-150), false, false, ""}, + leTestCase{float64(0), false, false, ""}, + leTestCase{float64(17), false, false, ""}, + leTestCase{float64(160), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) PositiveFloatLiteral() { + matcher := LessOrEqual(149.9) + desc := matcher.Description() + expectedDesc := "less than or equal to 149.9" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-1, true, false, ""}, + leTestCase{149, true, false, ""}, + leTestCase{149.9, true, false, ""}, + leTestCase{150, false, false, ""}, + leTestCase{151, false, false, ""}, + + leTestCase{int(-1), true, false, ""}, + leTestCase{int(149), true, false, ""}, + leTestCase{int(150), false, false, ""}, + leTestCase{int(151), false, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(0), true, false, ""}, + leTestCase{int8(17), true, false, ""}, + leTestCase{int8(127), true, false, ""}, + + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(149), true, false, ""}, + leTestCase{int16(150), false, false, ""}, + leTestCase{int16(151), false, false, ""}, + + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(149), true, false, ""}, + leTestCase{int32(150), false, false, ""}, + leTestCase{int32(151), false, false, ""}, + + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(149), true, false, ""}, + leTestCase{int64(150), false, false, ""}, + leTestCase{int64(151), false, false, ""}, + + // Unsigned integers. + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(149), true, false, ""}, + leTestCase{uint(150), false, false, ""}, + leTestCase{uint(151), false, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(127), true, false, ""}, + + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(149), true, false, ""}, + leTestCase{uint16(150), false, false, ""}, + leTestCase{uint16(151), false, false, ""}, + + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(149), true, false, ""}, + leTestCase{uint32(150), false, false, ""}, + leTestCase{uint32(151), false, false, ""}, + + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(149), true, false, ""}, + leTestCase{uint64(150), false, false, ""}, + leTestCase{uint64(151), false, false, ""}, + + // Floating point. + leTestCase{float32(-1), true, false, ""}, + leTestCase{float32(149), true, false, ""}, + leTestCase{float32(149.8), true, false, ""}, + leTestCase{float32(149.9), true, false, ""}, + leTestCase{float32(150), false, false, ""}, + leTestCase{float32(151), false, false, ""}, + + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(149), true, false, ""}, + leTestCase{float64(149.8), true, false, ""}, + leTestCase{float64(149.9), true, false, ""}, + leTestCase{float64(150), false, false, ""}, + leTestCase{float64(151), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Subtle cases +//////////////////////////////////////////////////////////////////////// + +func (t *LessOrEqualTest) Int64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := LessOrEqual(int64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "less than or equal to 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-1, true, false, ""}, + leTestCase{kTwoTo25 + 0, true, false, ""}, + leTestCase{kTwoTo25 + 1, true, false, ""}, + leTestCase{kTwoTo25 + 2, false, false, ""}, + + leTestCase{int(-1), true, false, ""}, + leTestCase{int(kTwoTo25 + 0), true, false, ""}, + leTestCase{int(kTwoTo25 + 1), true, false, ""}, + leTestCase{int(kTwoTo25 + 2), false, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(127), true, false, ""}, + + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(0), true, false, ""}, + leTestCase{int16(32767), true, false, ""}, + + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(kTwoTo25 + 0), true, false, ""}, + leTestCase{int32(kTwoTo25 + 1), true, false, ""}, + leTestCase{int32(kTwoTo25 + 2), false, false, ""}, + + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(kTwoTo25 + 0), true, false, ""}, + leTestCase{int64(kTwoTo25 + 1), true, false, ""}, + leTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + // Unsigned integers. + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint(kTwoTo25 + 2), false, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(255), true, false, ""}, + + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(65535), true, false, ""}, + + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint32(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint32(kTwoTo25 + 2), false, false, ""}, + + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Floating point. + leTestCase{float32(-1), true, false, ""}, + leTestCase{float32(kTwoTo25 - 2), true, false, ""}, + leTestCase{float32(kTwoTo25 - 1), true, false, ""}, + leTestCase{float32(kTwoTo25 + 0), true, false, ""}, + leTestCase{float32(kTwoTo25 + 1), true, false, ""}, + leTestCase{float32(kTwoTo25 + 2), true, false, ""}, + leTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(kTwoTo25 - 2), true, false, ""}, + leTestCase{float64(kTwoTo25 - 1), true, false, ""}, + leTestCase{float64(kTwoTo25 + 0), true, false, ""}, + leTestCase{float64(kTwoTo25 + 1), true, false, ""}, + leTestCase{float64(kTwoTo25 + 2), false, false, ""}, + leTestCase{float64(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) Int64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := LessOrEqual(int64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "less than or equal to 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-1, true, false, ""}, + leTestCase{1 << 30, true, false, ""}, + + leTestCase{int(-1), true, false, ""}, + leTestCase{int(math.MaxInt32), true, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(127), true, false, ""}, + + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(0), true, false, ""}, + leTestCase{int16(32767), true, false, ""}, + + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(math.MaxInt32), true, false, ""}, + + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(kTwoTo54 - 1), true, false, ""}, + leTestCase{int64(kTwoTo54 + 0), true, false, ""}, + leTestCase{int64(kTwoTo54 + 1), true, false, ""}, + leTestCase{int64(kTwoTo54 + 2), false, false, ""}, + + // Unsigned integers. + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(math.MaxUint32), true, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(255), true, false, ""}, + + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(65535), true, false, ""}, + + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(math.MaxUint32), true, false, ""}, + + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + + // Floating point. + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(kTwoTo54 - 2), true, false, ""}, + leTestCase{float64(kTwoTo54 - 1), true, false, ""}, + leTestCase{float64(kTwoTo54 + 0), true, false, ""}, + leTestCase{float64(kTwoTo54 + 1), true, false, ""}, + leTestCase{float64(kTwoTo54 + 2), true, false, ""}, + leTestCase{float64(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) Uint64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := LessOrEqual(uint64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "less than or equal to 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-1, true, false, ""}, + leTestCase{kTwoTo25 + 0, true, false, ""}, + leTestCase{kTwoTo25 + 1, true, false, ""}, + leTestCase{kTwoTo25 + 2, false, false, ""}, + + leTestCase{int(-1), true, false, ""}, + leTestCase{int(kTwoTo25 + 0), true, false, ""}, + leTestCase{int(kTwoTo25 + 1), true, false, ""}, + leTestCase{int(kTwoTo25 + 2), false, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(127), true, false, ""}, + + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(0), true, false, ""}, + leTestCase{int16(32767), true, false, ""}, + + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(kTwoTo25 + 0), true, false, ""}, + leTestCase{int32(kTwoTo25 + 1), true, false, ""}, + leTestCase{int32(kTwoTo25 + 2), false, false, ""}, + + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(kTwoTo25 + 0), true, false, ""}, + leTestCase{int64(kTwoTo25 + 1), true, false, ""}, + leTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + // Unsigned integers. + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint(kTwoTo25 + 2), false, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(255), true, false, ""}, + + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(65535), true, false, ""}, + + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint32(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint32(kTwoTo25 + 2), false, false, ""}, + + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Floating point. + leTestCase{float32(-1), true, false, ""}, + leTestCase{float32(kTwoTo25 - 2), true, false, ""}, + leTestCase{float32(kTwoTo25 - 1), true, false, ""}, + leTestCase{float32(kTwoTo25 + 0), true, false, ""}, + leTestCase{float32(kTwoTo25 + 1), true, false, ""}, + leTestCase{float32(kTwoTo25 + 2), true, false, ""}, + leTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(kTwoTo25 - 2), true, false, ""}, + leTestCase{float64(kTwoTo25 - 1), true, false, ""}, + leTestCase{float64(kTwoTo25 + 0), true, false, ""}, + leTestCase{float64(kTwoTo25 + 1), true, false, ""}, + leTestCase{float64(kTwoTo25 + 2), false, false, ""}, + leTestCase{float64(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) Uint64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := LessOrEqual(uint64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "less than or equal to 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{-1, true, false, ""}, + leTestCase{1 << 30, true, false, ""}, + + leTestCase{int(-1), true, false, ""}, + leTestCase{int(math.MaxInt32), true, false, ""}, + + leTestCase{int8(-1), true, false, ""}, + leTestCase{int8(127), true, false, ""}, + + leTestCase{int16(-1), true, false, ""}, + leTestCase{int16(0), true, false, ""}, + leTestCase{int16(32767), true, false, ""}, + + leTestCase{int32(-1), true, false, ""}, + leTestCase{int32(math.MaxInt32), true, false, ""}, + + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(kTwoTo54 - 1), true, false, ""}, + leTestCase{int64(kTwoTo54 + 0), true, false, ""}, + leTestCase{int64(kTwoTo54 + 1), true, false, ""}, + leTestCase{int64(kTwoTo54 + 2), false, false, ""}, + + // Unsigned integers. + leTestCase{uint(0), true, false, ""}, + leTestCase{uint(math.MaxUint32), true, false, ""}, + + leTestCase{uint8(0), true, false, ""}, + leTestCase{uint8(255), true, false, ""}, + + leTestCase{uint16(0), true, false, ""}, + leTestCase{uint16(65535), true, false, ""}, + + leTestCase{uint32(0), true, false, ""}, + leTestCase{uint32(math.MaxUint32), true, false, ""}, + + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + + // Floating point. + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(kTwoTo54 - 2), true, false, ""}, + leTestCase{float64(kTwoTo54 - 1), true, false, ""}, + leTestCase{float64(kTwoTo54 + 0), true, false, ""}, + leTestCase{float64(kTwoTo54 + 1), true, false, ""}, + leTestCase{float64(kTwoTo54 + 2), true, false, ""}, + leTestCase{float64(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) Float32AboveExactIntegerRange() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := LessOrEqual(float32(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "less than or equal to 3.3554432e+07" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(kTwoTo25 - 2), true, false, ""}, + leTestCase{int64(kTwoTo25 - 1), true, false, ""}, + leTestCase{int64(kTwoTo25 + 0), true, false, ""}, + leTestCase{int64(kTwoTo25 + 1), true, false, ""}, + leTestCase{int64(kTwoTo25 + 2), true, false, ""}, + leTestCase{int64(kTwoTo25 + 3), false, false, ""}, + + // Unsigned integers. + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(kTwoTo25 - 2), true, false, ""}, + leTestCase{uint64(kTwoTo25 - 1), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 1), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 2), true, false, ""}, + leTestCase{uint64(kTwoTo25 + 3), false, false, ""}, + + // Floating point. + leTestCase{float32(-1), true, false, ""}, + leTestCase{float32(kTwoTo25 - 2), true, false, ""}, + leTestCase{float32(kTwoTo25 - 1), true, false, ""}, + leTestCase{float32(kTwoTo25 + 0), true, false, ""}, + leTestCase{float32(kTwoTo25 + 1), true, false, ""}, + leTestCase{float32(kTwoTo25 + 2), true, false, ""}, + leTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(kTwoTo25 - 2), true, false, ""}, + leTestCase{float64(kTwoTo25 - 1), true, false, ""}, + leTestCase{float64(kTwoTo25 + 0), true, false, ""}, + leTestCase{float64(kTwoTo25 + 1), true, false, ""}, + leTestCase{float64(kTwoTo25 + 2), true, false, ""}, + leTestCase{float64(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) Float64AboveExactIntegerRange() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := LessOrEqual(float64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "less than or equal to 1.8014398509481984e+16" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + // Signed integers. + leTestCase{int64(-1), true, false, ""}, + leTestCase{int64(kTwoTo54 - 2), true, false, ""}, + leTestCase{int64(kTwoTo54 - 1), true, false, ""}, + leTestCase{int64(kTwoTo54 + 0), true, false, ""}, + leTestCase{int64(kTwoTo54 + 1), true, false, ""}, + leTestCase{int64(kTwoTo54 + 2), true, false, ""}, + leTestCase{int64(kTwoTo54 + 3), false, false, ""}, + + // Unsigned integers. + leTestCase{uint64(0), true, false, ""}, + leTestCase{uint64(kTwoTo54 - 2), true, false, ""}, + leTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 1), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 2), true, false, ""}, + leTestCase{uint64(kTwoTo54 + 3), false, false, ""}, + + // Floating point. + leTestCase{float64(-1), true, false, ""}, + leTestCase{float64(kTwoTo54 - 2), true, false, ""}, + leTestCase{float64(kTwoTo54 - 1), true, false, ""}, + leTestCase{float64(kTwoTo54 + 0), true, false, ""}, + leTestCase{float64(kTwoTo54 + 1), true, false, ""}, + leTestCase{float64(kTwoTo54 + 2), true, false, ""}, + leTestCase{float64(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// String literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessOrEqualTest) EmptyString() { + matcher := LessOrEqual("") + desc := matcher.Description() + expectedDesc := "less than or equal to \"\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + leTestCase{"", true, false, ""}, + leTestCase{"\x00", false, false, ""}, + leTestCase{"a", false, false, ""}, + leTestCase{"foo", false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) SingleNullByte() { + matcher := LessOrEqual("\x00") + desc := matcher.Description() + expectedDesc := "less than or equal to \"\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + leTestCase{"", true, false, ""}, + leTestCase{"\x00", true, false, ""}, + leTestCase{"\x00\x00", false, false, ""}, + leTestCase{"a", false, false, ""}, + leTestCase{"foo", false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessOrEqualTest) LongerString() { + matcher := LessOrEqual("foo\x00") + desc := matcher.Description() + expectedDesc := "less than or equal to \"foo\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []leTestCase{ + leTestCase{"", true, false, ""}, + leTestCase{"\x00", true, false, ""}, + leTestCase{"bar", true, false, ""}, + leTestCase{"foo", true, false, ""}, + leTestCase{"foo\x00", true, false, ""}, + leTestCase{"foo\x00\x00", false, false, ""}, + leTestCase{"fooa", false, false, ""}, + leTestCase{"qux", false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go new file mode 100644 index 0000000..8258e45 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go @@ -0,0 +1,152 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "math" + "reflect" +) + +// LessThan returns a matcher that matches integer, floating point, or strings +// values v such that v < x. Comparison is not defined between numeric and +// string types, but is defined between all integer and floating point types. +// +// x must itself be an integer, floating point, or string type; otherwise, +// LessThan will panic. +func LessThan(x interface{}) Matcher { + v := reflect.ValueOf(x) + kind := v.Kind() + + switch { + case isInteger(v): + case isFloat(v): + case kind == reflect.String: + + default: + panic(fmt.Sprintf("LessThan: unexpected kind %v", kind)) + } + + return &lessThanMatcher{v} +} + +type lessThanMatcher struct { + limit reflect.Value +} + +func (m *lessThanMatcher) Description() string { + // Special case: make it clear that strings are strings. + if m.limit.Kind() == reflect.String { + return fmt.Sprintf("less than \"%s\"", m.limit.String()) + } + + return fmt.Sprintf("less than %v", m.limit.Interface()) +} + +func compareIntegers(v1, v2 reflect.Value) (err error) { + err = errors.New("") + + switch { + case isSignedInteger(v1) && isSignedInteger(v2): + if v1.Int() < v2.Int() { + err = nil + } + return + + case isSignedInteger(v1) && isUnsignedInteger(v2): + if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() { + err = nil + } + return + + case isUnsignedInteger(v1) && isSignedInteger(v2): + if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() { + err = nil + } + return + + case isUnsignedInteger(v1) && isUnsignedInteger(v2): + if v1.Uint() < v2.Uint() { + err = nil + } + return + } + + panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2)) +} + +func getFloat(v reflect.Value) float64 { + switch { + case isSignedInteger(v): + return float64(v.Int()) + + case isUnsignedInteger(v): + return float64(v.Uint()) + + case isFloat(v): + return v.Float() + } + + panic(fmt.Sprintf("getFloat: %v", v)) +} + +func (m *lessThanMatcher) Matches(c interface{}) (err error) { + v1 := reflect.ValueOf(c) + v2 := m.limit + + err = errors.New("") + + // Handle strings as a special case. + if v1.Kind() == reflect.String && v2.Kind() == reflect.String { + if v1.String() < v2.String() { + err = nil + } + return + } + + // If we get here, we require that we are dealing with integers or floats. + v1Legal := isInteger(v1) || isFloat(v1) + v2Legal := isInteger(v2) || isFloat(v2) + if !v1Legal || !v2Legal { + err = NewFatalError("which is not comparable") + return + } + + // Handle the various comparison cases. + switch { + // Both integers + case isInteger(v1) && isInteger(v2): + return compareIntegers(v1, v2) + + // At least one float32 + case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32: + if float32(getFloat(v1)) < float32(getFloat(v2)) { + err = nil + } + return + + // At least one float64 + case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64: + if getFloat(v1) < getFloat(v2) { + err = nil + } + return + } + + // We shouldn't get here. + panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than_test.go new file mode 100644 index 0000000..7de5bd9 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than_test.go @@ -0,0 +1,1057 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "math" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type LessThanTest struct { +} + +func init() { RegisterTestSuite(&LessThanTest{}) } + +type ltTestCase struct { + candidate interface{} + expectedResult bool + shouldBeFatal bool + expectedError string +} + +func (t *LessThanTest) checkTestCases(matcher Matcher, cases []ltTestCase) { + for i, c := range cases { + err := matcher.Matches(c.candidate) + + ExpectThat( + (err == nil), + Equals(c.expectedResult), + "Case %d (candidate %v)", + i, + c.candidate) + + if err == nil { + continue + } + + _, isFatal := err.(*FatalError) + ExpectEq( + c.shouldBeFatal, + isFatal, + "Case %d (candidate %v)", + i, + c.candidate) + + ExpectThat( + err, + Error(Equals(c.expectedError)), + "Case %d (candidate %v)", + i, + c.candidate) + } +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessThanTest) IntegerCandidateBadTypes() { + matcher := LessThan(int(-150)) + + cases := []ltTestCase{ + ltTestCase{true, false, true, "which is not comparable"}, + ltTestCase{complex64(-151), false, true, "which is not comparable"}, + ltTestCase{complex128(-151), false, true, "which is not comparable"}, + ltTestCase{[...]int{-151}, false, true, "which is not comparable"}, + ltTestCase{make(chan int), false, true, "which is not comparable"}, + ltTestCase{func() {}, false, true, "which is not comparable"}, + ltTestCase{map[int]int{}, false, true, "which is not comparable"}, + ltTestCase{<TestCase{}, false, true, "which is not comparable"}, + ltTestCase{make([]int, 0), false, true, "which is not comparable"}, + ltTestCase{"-151", false, true, "which is not comparable"}, + ltTestCase{ltTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) FloatCandidateBadTypes() { + matcher := LessThan(float32(-150)) + + cases := []ltTestCase{ + ltTestCase{true, false, true, "which is not comparable"}, + ltTestCase{complex64(-151), false, true, "which is not comparable"}, + ltTestCase{complex128(-151), false, true, "which is not comparable"}, + ltTestCase{[...]int{-151}, false, true, "which is not comparable"}, + ltTestCase{make(chan int), false, true, "which is not comparable"}, + ltTestCase{func() {}, false, true, "which is not comparable"}, + ltTestCase{map[int]int{}, false, true, "which is not comparable"}, + ltTestCase{<TestCase{}, false, true, "which is not comparable"}, + ltTestCase{make([]int, 0), false, true, "which is not comparable"}, + ltTestCase{"-151", false, true, "which is not comparable"}, + ltTestCase{ltTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) StringCandidateBadTypes() { + matcher := LessThan("17") + + cases := []ltTestCase{ + ltTestCase{true, false, true, "which is not comparable"}, + ltTestCase{int(0), false, true, "which is not comparable"}, + ltTestCase{int8(0), false, true, "which is not comparable"}, + ltTestCase{int16(0), false, true, "which is not comparable"}, + ltTestCase{int32(0), false, true, "which is not comparable"}, + ltTestCase{int64(0), false, true, "which is not comparable"}, + ltTestCase{uint(0), false, true, "which is not comparable"}, + ltTestCase{uint8(0), false, true, "which is not comparable"}, + ltTestCase{uint16(0), false, true, "which is not comparable"}, + ltTestCase{uint32(0), false, true, "which is not comparable"}, + ltTestCase{uint64(0), false, true, "which is not comparable"}, + ltTestCase{float32(0), false, true, "which is not comparable"}, + ltTestCase{float64(0), false, true, "which is not comparable"}, + ltTestCase{complex64(-151), false, true, "which is not comparable"}, + ltTestCase{complex128(-151), false, true, "which is not comparable"}, + ltTestCase{[...]int{-151}, false, true, "which is not comparable"}, + ltTestCase{make(chan int), false, true, "which is not comparable"}, + ltTestCase{func() {}, false, true, "which is not comparable"}, + ltTestCase{map[int]int{}, false, true, "which is not comparable"}, + ltTestCase{<TestCase{}, false, true, "which is not comparable"}, + ltTestCase{make([]int, 0), false, true, "which is not comparable"}, + ltTestCase{ltTestCase{}, false, true, "which is not comparable"}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) BadArgument() { + panicked := false + + defer func() { + ExpectThat(panicked, Equals(true)) + }() + + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + + LessThan(complex128(0)) +} + +//////////////////////////////////////////////////////////////////////// +// Integer literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessThanTest) NegativeIntegerLiteral() { + matcher := LessThan(-150) + desc := matcher.Description() + expectedDesc := "less than -150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-(1 << 30), true, false, ""}, + ltTestCase{-151, true, false, ""}, + ltTestCase{-150, false, false, ""}, + ltTestCase{0, false, false, ""}, + ltTestCase{17, false, false, ""}, + + ltTestCase{int(-(1 << 30)), true, false, ""}, + ltTestCase{int(-151), true, false, ""}, + ltTestCase{int(-150), false, false, ""}, + ltTestCase{int(0), false, false, ""}, + ltTestCase{int(17), false, false, ""}, + + ltTestCase{int8(-127), false, false, ""}, + ltTestCase{int8(0), false, false, ""}, + ltTestCase{int8(17), false, false, ""}, + + ltTestCase{int16(-(1 << 14)), true, false, ""}, + ltTestCase{int16(-151), true, false, ""}, + ltTestCase{int16(-150), false, false, ""}, + ltTestCase{int16(0), false, false, ""}, + ltTestCase{int16(17), false, false, ""}, + + ltTestCase{int32(-(1 << 30)), true, false, ""}, + ltTestCase{int32(-151), true, false, ""}, + ltTestCase{int32(-150), false, false, ""}, + ltTestCase{int32(0), false, false, ""}, + ltTestCase{int32(17), false, false, ""}, + + ltTestCase{int64(-(1 << 30)), true, false, ""}, + ltTestCase{int64(-151), true, false, ""}, + ltTestCase{int64(-150), false, false, ""}, + ltTestCase{int64(0), false, false, ""}, + ltTestCase{int64(17), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint((1 << 32) - 151), false, false, ""}, + ltTestCase{uint(0), false, false, ""}, + ltTestCase{uint(17), false, false, ""}, + + ltTestCase{uint8(0), false, false, ""}, + ltTestCase{uint8(17), false, false, ""}, + ltTestCase{uint8(253), false, false, ""}, + + ltTestCase{uint16((1 << 16) - 151), false, false, ""}, + ltTestCase{uint16(0), false, false, ""}, + ltTestCase{uint16(17), false, false, ""}, + + ltTestCase{uint32((1 << 32) - 151), false, false, ""}, + ltTestCase{uint32(0), false, false, ""}, + ltTestCase{uint32(17), false, false, ""}, + + ltTestCase{uint64((1 << 64) - 151), false, false, ""}, + ltTestCase{uint64(0), false, false, ""}, + ltTestCase{uint64(17), false, false, ""}, + + // Floating point. + ltTestCase{float32(-(1 << 30)), true, false, ""}, + ltTestCase{float32(-151), true, false, ""}, + ltTestCase{float32(-150.1), true, false, ""}, + ltTestCase{float32(-150), false, false, ""}, + ltTestCase{float32(-149.9), false, false, ""}, + ltTestCase{float32(0), false, false, ""}, + ltTestCase{float32(17), false, false, ""}, + ltTestCase{float32(160), false, false, ""}, + + ltTestCase{float64(-(1 << 30)), true, false, ""}, + ltTestCase{float64(-151), true, false, ""}, + ltTestCase{float64(-150.1), true, false, ""}, + ltTestCase{float64(-150), false, false, ""}, + ltTestCase{float64(-149.9), false, false, ""}, + ltTestCase{float64(0), false, false, ""}, + ltTestCase{float64(17), false, false, ""}, + ltTestCase{float64(160), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) ZeroIntegerLiteral() { + matcher := LessThan(0) + desc := matcher.Description() + expectedDesc := "less than 0" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-(1 << 30), true, false, ""}, + ltTestCase{-1, true, false, ""}, + ltTestCase{0, false, false, ""}, + ltTestCase{1, false, false, ""}, + ltTestCase{17, false, false, ""}, + ltTestCase{(1 << 30), false, false, ""}, + + ltTestCase{int(-(1 << 30)), true, false, ""}, + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(0), false, false, ""}, + ltTestCase{int(1), false, false, ""}, + ltTestCase{int(17), false, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(0), false, false, ""}, + ltTestCase{int8(1), false, false, ""}, + + ltTestCase{int16(-(1 << 14)), true, false, ""}, + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(0), false, false, ""}, + ltTestCase{int16(1), false, false, ""}, + ltTestCase{int16(17), false, false, ""}, + + ltTestCase{int32(-(1 << 30)), true, false, ""}, + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(0), false, false, ""}, + ltTestCase{int32(1), false, false, ""}, + ltTestCase{int32(17), false, false, ""}, + + ltTestCase{int64(-(1 << 30)), true, false, ""}, + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(0), false, false, ""}, + ltTestCase{int64(1), false, false, ""}, + ltTestCase{int64(17), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint((1 << 32) - 1), false, false, ""}, + ltTestCase{uint(0), false, false, ""}, + ltTestCase{uint(17), false, false, ""}, + + ltTestCase{uint8(0), false, false, ""}, + ltTestCase{uint8(17), false, false, ""}, + ltTestCase{uint8(253), false, false, ""}, + + ltTestCase{uint16((1 << 16) - 1), false, false, ""}, + ltTestCase{uint16(0), false, false, ""}, + ltTestCase{uint16(17), false, false, ""}, + + ltTestCase{uint32((1 << 32) - 1), false, false, ""}, + ltTestCase{uint32(0), false, false, ""}, + ltTestCase{uint32(17), false, false, ""}, + + ltTestCase{uint64((1 << 64) - 1), false, false, ""}, + ltTestCase{uint64(0), false, false, ""}, + ltTestCase{uint64(17), false, false, ""}, + + // Floating point. + ltTestCase{float32(-(1 << 30)), true, false, ""}, + ltTestCase{float32(-1), true, false, ""}, + ltTestCase{float32(-0.1), true, false, ""}, + ltTestCase{float32(-0.0), false, false, ""}, + ltTestCase{float32(0), false, false, ""}, + ltTestCase{float32(0.1), false, false, ""}, + ltTestCase{float32(17), false, false, ""}, + ltTestCase{float32(160), false, false, ""}, + + ltTestCase{float64(-(1 << 30)), true, false, ""}, + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(-0.1), true, false, ""}, + ltTestCase{float64(-0), false, false, ""}, + ltTestCase{float64(0), false, false, ""}, + ltTestCase{float64(17), false, false, ""}, + ltTestCase{float64(160), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) PositiveIntegerLiteral() { + matcher := LessThan(150) + desc := matcher.Description() + expectedDesc := "less than 150" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-1, true, false, ""}, + ltTestCase{149, true, false, ""}, + ltTestCase{150, false, false, ""}, + ltTestCase{151, false, false, ""}, + + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(149), true, false, ""}, + ltTestCase{int(150), false, false, ""}, + ltTestCase{int(151), false, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(0), true, false, ""}, + ltTestCase{int8(17), true, false, ""}, + ltTestCase{int8(127), true, false, ""}, + + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(149), true, false, ""}, + ltTestCase{int16(150), false, false, ""}, + ltTestCase{int16(151), false, false, ""}, + + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(149), true, false, ""}, + ltTestCase{int32(150), false, false, ""}, + ltTestCase{int32(151), false, false, ""}, + + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(149), true, false, ""}, + ltTestCase{int64(150), false, false, ""}, + ltTestCase{int64(151), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint(0), true, false, ""}, + ltTestCase{uint(149), true, false, ""}, + ltTestCase{uint(150), false, false, ""}, + ltTestCase{uint(151), false, false, ""}, + + ltTestCase{uint8(0), true, false, ""}, + ltTestCase{uint8(127), true, false, ""}, + + ltTestCase{uint16(0), true, false, ""}, + ltTestCase{uint16(149), true, false, ""}, + ltTestCase{uint16(150), false, false, ""}, + ltTestCase{uint16(151), false, false, ""}, + + ltTestCase{uint32(0), true, false, ""}, + ltTestCase{uint32(149), true, false, ""}, + ltTestCase{uint32(150), false, false, ""}, + ltTestCase{uint32(151), false, false, ""}, + + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(149), true, false, ""}, + ltTestCase{uint64(150), false, false, ""}, + ltTestCase{uint64(151), false, false, ""}, + + // Floating point. + ltTestCase{float32(-1), true, false, ""}, + ltTestCase{float32(149), true, false, ""}, + ltTestCase{float32(149.9), true, false, ""}, + ltTestCase{float32(150), false, false, ""}, + ltTestCase{float32(150.1), false, false, ""}, + ltTestCase{float32(151), false, false, ""}, + + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(149), true, false, ""}, + ltTestCase{float64(149.9), true, false, ""}, + ltTestCase{float64(150), false, false, ""}, + ltTestCase{float64(150.1), false, false, ""}, + ltTestCase{float64(151), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Float literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessThanTest) NegativeFloatLiteral() { + matcher := LessThan(-150.1) + desc := matcher.Description() + expectedDesc := "less than -150.1" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-(1 << 30), true, false, ""}, + ltTestCase{-151, true, false, ""}, + ltTestCase{-150, false, false, ""}, + ltTestCase{0, false, false, ""}, + ltTestCase{17, false, false, ""}, + + ltTestCase{int(-(1 << 30)), true, false, ""}, + ltTestCase{int(-151), true, false, ""}, + ltTestCase{int(-150), false, false, ""}, + ltTestCase{int(0), false, false, ""}, + ltTestCase{int(17), false, false, ""}, + + ltTestCase{int8(-127), false, false, ""}, + ltTestCase{int8(0), false, false, ""}, + ltTestCase{int8(17), false, false, ""}, + + ltTestCase{int16(-(1 << 14)), true, false, ""}, + ltTestCase{int16(-151), true, false, ""}, + ltTestCase{int16(-150), false, false, ""}, + ltTestCase{int16(0), false, false, ""}, + ltTestCase{int16(17), false, false, ""}, + + ltTestCase{int32(-(1 << 30)), true, false, ""}, + ltTestCase{int32(-151), true, false, ""}, + ltTestCase{int32(-150), false, false, ""}, + ltTestCase{int32(0), false, false, ""}, + ltTestCase{int32(17), false, false, ""}, + + ltTestCase{int64(-(1 << 30)), true, false, ""}, + ltTestCase{int64(-151), true, false, ""}, + ltTestCase{int64(-150), false, false, ""}, + ltTestCase{int64(0), false, false, ""}, + ltTestCase{int64(17), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint((1 << 32) - 151), false, false, ""}, + ltTestCase{uint(0), false, false, ""}, + ltTestCase{uint(17), false, false, ""}, + + ltTestCase{uint8(0), false, false, ""}, + ltTestCase{uint8(17), false, false, ""}, + ltTestCase{uint8(253), false, false, ""}, + + ltTestCase{uint16((1 << 16) - 151), false, false, ""}, + ltTestCase{uint16(0), false, false, ""}, + ltTestCase{uint16(17), false, false, ""}, + + ltTestCase{uint32((1 << 32) - 151), false, false, ""}, + ltTestCase{uint32(0), false, false, ""}, + ltTestCase{uint32(17), false, false, ""}, + + ltTestCase{uint64((1 << 64) - 151), false, false, ""}, + ltTestCase{uint64(0), false, false, ""}, + ltTestCase{uint64(17), false, false, ""}, + + // Floating point. + ltTestCase{float32(-(1 << 30)), true, false, ""}, + ltTestCase{float32(-151), true, false, ""}, + ltTestCase{float32(-150.2), true, false, ""}, + ltTestCase{float32(-150.1), false, false, ""}, + ltTestCase{float32(-150), false, false, ""}, + ltTestCase{float32(0), false, false, ""}, + ltTestCase{float32(17), false, false, ""}, + ltTestCase{float32(160), false, false, ""}, + + ltTestCase{float64(-(1 << 30)), true, false, ""}, + ltTestCase{float64(-151), true, false, ""}, + ltTestCase{float64(-150.2), true, false, ""}, + ltTestCase{float64(-150.1), false, false, ""}, + ltTestCase{float64(-150), false, false, ""}, + ltTestCase{float64(0), false, false, ""}, + ltTestCase{float64(17), false, false, ""}, + ltTestCase{float64(160), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) PositiveFloatLiteral() { + matcher := LessThan(149.9) + desc := matcher.Description() + expectedDesc := "less than 149.9" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-1, true, false, ""}, + ltTestCase{149, true, false, ""}, + ltTestCase{150, false, false, ""}, + ltTestCase{151, false, false, ""}, + + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(149), true, false, ""}, + ltTestCase{int(150), false, false, ""}, + ltTestCase{int(151), false, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(0), true, false, ""}, + ltTestCase{int8(17), true, false, ""}, + ltTestCase{int8(127), true, false, ""}, + + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(149), true, false, ""}, + ltTestCase{int16(150), false, false, ""}, + ltTestCase{int16(151), false, false, ""}, + + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(149), true, false, ""}, + ltTestCase{int32(150), false, false, ""}, + ltTestCase{int32(151), false, false, ""}, + + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(149), true, false, ""}, + ltTestCase{int64(150), false, false, ""}, + ltTestCase{int64(151), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint(0), true, false, ""}, + ltTestCase{uint(149), true, false, ""}, + ltTestCase{uint(150), false, false, ""}, + ltTestCase{uint(151), false, false, ""}, + + ltTestCase{uint8(0), true, false, ""}, + ltTestCase{uint8(127), true, false, ""}, + + ltTestCase{uint16(0), true, false, ""}, + ltTestCase{uint16(149), true, false, ""}, + ltTestCase{uint16(150), false, false, ""}, + ltTestCase{uint16(151), false, false, ""}, + + ltTestCase{uint32(0), true, false, ""}, + ltTestCase{uint32(149), true, false, ""}, + ltTestCase{uint32(150), false, false, ""}, + ltTestCase{uint32(151), false, false, ""}, + + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(149), true, false, ""}, + ltTestCase{uint64(150), false, false, ""}, + ltTestCase{uint64(151), false, false, ""}, + + // Floating point. + ltTestCase{float32(-1), true, false, ""}, + ltTestCase{float32(149), true, false, ""}, + ltTestCase{float32(149.8), true, false, ""}, + ltTestCase{float32(149.9), false, false, ""}, + ltTestCase{float32(150), false, false, ""}, + ltTestCase{float32(151), false, false, ""}, + + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(149), true, false, ""}, + ltTestCase{float64(149.8), true, false, ""}, + ltTestCase{float64(149.9), false, false, ""}, + ltTestCase{float64(150), false, false, ""}, + ltTestCase{float64(151), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// Subtle cases +//////////////////////////////////////////////////////////////////////// + +func (t *LessThanTest) Int64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := LessThan(int64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "less than 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-1, true, false, ""}, + ltTestCase{kTwoTo25 + 0, true, false, ""}, + ltTestCase{kTwoTo25 + 1, false, false, ""}, + ltTestCase{kTwoTo25 + 2, false, false, ""}, + + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(kTwoTo25 + 0), true, false, ""}, + ltTestCase{int(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(127), true, false, ""}, + + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(0), true, false, ""}, + ltTestCase{int16(32767), true, false, ""}, + + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(kTwoTo25 + 0), true, false, ""}, + ltTestCase{int32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int32(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(kTwoTo25 + 0), true, false, ""}, + ltTestCase{int64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint(0), true, false, ""}, + ltTestCase{uint(kTwoTo25 + 0), true, false, ""}, + ltTestCase{uint(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{uint8(0), true, false, ""}, + ltTestCase{uint8(255), true, false, ""}, + + ltTestCase{uint16(0), true, false, ""}, + ltTestCase{uint16(65535), true, false, ""}, + + ltTestCase{uint32(0), true, false, ""}, + ltTestCase{uint32(kTwoTo25 + 0), true, false, ""}, + ltTestCase{uint32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint32(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + ltTestCase{uint64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Floating point. + ltTestCase{float32(-1), true, false, ""}, + ltTestCase{float32(kTwoTo25 - 2), true, false, ""}, + ltTestCase{float32(kTwoTo25 - 1), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 0), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 2), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(kTwoTo25 - 2), true, false, ""}, + ltTestCase{float64(kTwoTo25 - 1), true, false, ""}, + ltTestCase{float64(kTwoTo25 + 0), true, false, ""}, + ltTestCase{float64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 2), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) Int64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := LessThan(int64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "less than 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-1, true, false, ""}, + ltTestCase{1 << 30, true, false, ""}, + + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(math.MaxInt32), true, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(127), true, false, ""}, + + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(0), true, false, ""}, + ltTestCase{int16(32767), true, false, ""}, + + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(math.MaxInt32), true, false, ""}, + + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(kTwoTo54 - 1), true, false, ""}, + ltTestCase{int64(kTwoTo54 + 0), true, false, ""}, + ltTestCase{int64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{int64(kTwoTo54 + 2), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint(0), true, false, ""}, + ltTestCase{uint(math.MaxUint32), true, false, ""}, + + ltTestCase{uint8(0), true, false, ""}, + ltTestCase{uint8(255), true, false, ""}, + + ltTestCase{uint16(0), true, false, ""}, + ltTestCase{uint16(65535), true, false, ""}, + + ltTestCase{uint32(0), true, false, ""}, + ltTestCase{uint32(math.MaxUint32), true, false, ""}, + + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + ltTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + ltTestCase{uint64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + + // Floating point. + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(kTwoTo54 - 2), true, false, ""}, + ltTestCase{float64(kTwoTo54 - 1), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 0), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 2), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) Uint64NotExactlyRepresentableBySinglePrecision() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := LessThan(uint64(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "less than 33554433" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-1, true, false, ""}, + ltTestCase{kTwoTo25 + 0, true, false, ""}, + ltTestCase{kTwoTo25 + 1, false, false, ""}, + ltTestCase{kTwoTo25 + 2, false, false, ""}, + + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(kTwoTo25 + 0), true, false, ""}, + ltTestCase{int(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(127), true, false, ""}, + + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(0), true, false, ""}, + ltTestCase{int16(32767), true, false, ""}, + + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(kTwoTo25 + 0), true, false, ""}, + ltTestCase{int32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int32(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(kTwoTo25 + 0), true, false, ""}, + ltTestCase{int64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int64(kTwoTo25 + 2), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint(0), true, false, ""}, + ltTestCase{uint(kTwoTo25 + 0), true, false, ""}, + ltTestCase{uint(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{uint8(0), true, false, ""}, + ltTestCase{uint8(255), true, false, ""}, + + ltTestCase{uint16(0), true, false, ""}, + ltTestCase{uint16(65535), true, false, ""}, + + ltTestCase{uint32(0), true, false, ""}, + ltTestCase{uint32(kTwoTo25 + 0), true, false, ""}, + ltTestCase{uint32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint32(kTwoTo25 + 2), false, false, ""}, + + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(kTwoTo25 + 0), true, false, ""}, + ltTestCase{uint64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + + // Floating point. + ltTestCase{float32(-1), true, false, ""}, + ltTestCase{float32(kTwoTo25 - 2), true, false, ""}, + ltTestCase{float32(kTwoTo25 - 1), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 0), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 2), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(kTwoTo25 - 2), true, false, ""}, + ltTestCase{float64(kTwoTo25 - 1), true, false, ""}, + ltTestCase{float64(kTwoTo25 + 0), true, false, ""}, + ltTestCase{float64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 2), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) Uint64NotExactlyRepresentableByDoublePrecision() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := LessThan(uint64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "less than 18014398509481985" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{-1, true, false, ""}, + ltTestCase{1 << 30, true, false, ""}, + + ltTestCase{int(-1), true, false, ""}, + ltTestCase{int(math.MaxInt32), true, false, ""}, + + ltTestCase{int8(-1), true, false, ""}, + ltTestCase{int8(127), true, false, ""}, + + ltTestCase{int16(-1), true, false, ""}, + ltTestCase{int16(0), true, false, ""}, + ltTestCase{int16(32767), true, false, ""}, + + ltTestCase{int32(-1), true, false, ""}, + ltTestCase{int32(math.MaxInt32), true, false, ""}, + + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(kTwoTo54 - 1), true, false, ""}, + ltTestCase{int64(kTwoTo54 + 0), true, false, ""}, + ltTestCase{int64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{int64(kTwoTo54 + 2), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint(0), true, false, ""}, + ltTestCase{uint(math.MaxUint32), true, false, ""}, + + ltTestCase{uint8(0), true, false, ""}, + ltTestCase{uint8(255), true, false, ""}, + + ltTestCase{uint16(0), true, false, ""}, + ltTestCase{uint16(65535), true, false, ""}, + + ltTestCase{uint32(0), true, false, ""}, + ltTestCase{uint32(math.MaxUint32), true, false, ""}, + + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(kTwoTo54 - 1), true, false, ""}, + ltTestCase{uint64(kTwoTo54 + 0), true, false, ""}, + ltTestCase{uint64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + + // Floating point. + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(kTwoTo54 - 2), true, false, ""}, + ltTestCase{float64(kTwoTo54 - 1), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 0), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 2), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) Float32AboveExactIntegerRange() { + // Single-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^25-1, 2^25+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo25 = 1 << 25 + matcher := LessThan(float32(kTwoTo25 + 1)) + + desc := matcher.Description() + expectedDesc := "less than 3.3554432e+07" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(kTwoTo25 - 2), true, false, ""}, + ltTestCase{int64(kTwoTo25 - 1), false, false, ""}, + ltTestCase{int64(kTwoTo25 + 0), false, false, ""}, + ltTestCase{int64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{int64(kTwoTo25 + 2), false, false, ""}, + ltTestCase{int64(kTwoTo25 + 3), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(kTwoTo25 - 2), true, false, ""}, + ltTestCase{uint64(kTwoTo25 - 1), false, false, ""}, + ltTestCase{uint64(kTwoTo25 + 0), false, false, ""}, + ltTestCase{uint64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{uint64(kTwoTo25 + 2), false, false, ""}, + ltTestCase{uint64(kTwoTo25 + 3), false, false, ""}, + + // Floating point. + ltTestCase{float32(-1), true, false, ""}, + ltTestCase{float32(kTwoTo25 - 2), true, false, ""}, + ltTestCase{float32(kTwoTo25 - 1), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 0), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 1), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 2), false, false, ""}, + ltTestCase{float32(kTwoTo25 + 3), false, false, ""}, + + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(kTwoTo25 - 2), true, false, ""}, + ltTestCase{float64(kTwoTo25 - 1), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 0), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 1), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 2), false, false, ""}, + ltTestCase{float64(kTwoTo25 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) Float64AboveExactIntegerRange() { + // Double-precision floats don't have enough bits to represent the integers + // near this one distinctly, so [2^54-1, 2^54+2] all receive the same value + // and should be treated as equivalent when floats are in the mix. + const kTwoTo54 = 1 << 54 + matcher := LessThan(float64(kTwoTo54 + 1)) + + desc := matcher.Description() + expectedDesc := "less than 1.8014398509481984e+16" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + // Signed integers. + ltTestCase{int64(-1), true, false, ""}, + ltTestCase{int64(kTwoTo54 - 2), true, false, ""}, + ltTestCase{int64(kTwoTo54 - 1), false, false, ""}, + ltTestCase{int64(kTwoTo54 + 0), false, false, ""}, + ltTestCase{int64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{int64(kTwoTo54 + 2), false, false, ""}, + ltTestCase{int64(kTwoTo54 + 3), false, false, ""}, + + // Unsigned integers. + ltTestCase{uint64(0), true, false, ""}, + ltTestCase{uint64(kTwoTo54 - 2), true, false, ""}, + ltTestCase{uint64(kTwoTo54 - 1), false, false, ""}, + ltTestCase{uint64(kTwoTo54 + 0), false, false, ""}, + ltTestCase{uint64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{uint64(kTwoTo54 + 2), false, false, ""}, + ltTestCase{uint64(kTwoTo54 + 3), false, false, ""}, + + // Floating point. + ltTestCase{float64(-1), true, false, ""}, + ltTestCase{float64(kTwoTo54 - 2), true, false, ""}, + ltTestCase{float64(kTwoTo54 - 1), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 0), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 1), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 2), false, false, ""}, + ltTestCase{float64(kTwoTo54 + 3), false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +//////////////////////////////////////////////////////////////////////// +// String literals +//////////////////////////////////////////////////////////////////////// + +func (t *LessThanTest) EmptyString() { + matcher := LessThan("") + desc := matcher.Description() + expectedDesc := "less than \"\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + ltTestCase{"", false, false, ""}, + ltTestCase{"\x00", false, false, ""}, + ltTestCase{"a", false, false, ""}, + ltTestCase{"foo", false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) SingleNullByte() { + matcher := LessThan("\x00") + desc := matcher.Description() + expectedDesc := "less than \"\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + ltTestCase{"", true, false, ""}, + ltTestCase{"\x00", false, false, ""}, + ltTestCase{"a", false, false, ""}, + ltTestCase{"foo", false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} + +func (t *LessThanTest) LongerString() { + matcher := LessThan("foo\x00") + desc := matcher.Description() + expectedDesc := "less than \"foo\x00\"" + + ExpectThat(desc, Equals(expectedDesc)) + + cases := []ltTestCase{ + ltTestCase{"", true, false, ""}, + ltTestCase{"\x00", true, false, ""}, + ltTestCase{"bar", true, false, ""}, + ltTestCase{"foo", true, false, ""}, + ltTestCase{"foo\x00", false, false, ""}, + ltTestCase{"fooa", false, false, ""}, + ltTestCase{"qux", false, false, ""}, + } + + t.checkTestCases(matcher, cases) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go new file mode 100644 index 0000000..78159a0 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go @@ -0,0 +1,86 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package oglematchers provides a set of matchers useful in a testing or +// mocking framework. These matchers are inspired by and mostly compatible with +// Google Test for C++ and Google JS Test. +// +// This package is used by github.com/smartystreets/assertions/internal/ogletest and +// github.com/smartystreets/assertions/internal/oglemock, which may be more directly useful if you're not +// writing your own testing package or defining your own matchers. +package oglematchers + +// A Matcher is some predicate implicitly defining a set of values that it +// matches. For example, GreaterThan(17) matches all numeric values greater +// than 17, and HasSubstr("taco") matches all strings with the substring +// "taco". +// +// Matchers are typically exposed to tests via constructor functions like +// HasSubstr. In order to implement such a function you can either define your +// own matcher type or use NewMatcher. +type Matcher interface { + // Check whether the supplied value belongs to the the set defined by the + // matcher. Return a non-nil error if and only if it does not. + // + // The error describes why the value doesn't match. The error text is a + // relative clause that is suitable for being placed after the value. For + // example, a predicate that matches strings with a particular substring may, + // when presented with a numerical value, return the following error text: + // + // "which is not a string" + // + // Then the failure message may look like: + // + // Expected: has substring "taco" + // Actual: 17, which is not a string + // + // If the error is self-apparent based on the description of the matcher, the + // error text may be empty (but the error still non-nil). For example: + // + // Expected: 17 + // Actual: 19 + // + // If you are implementing a new matcher, see also the documentation on + // FatalError. + Matches(candidate interface{}) error + + // Description returns a string describing the property that values matching + // this matcher have, as a verb phrase where the subject is the value. For + // example, "is greather than 17" or "has substring "taco"". + Description() string +} + +// FatalError is an implementation of the error interface that may be returned +// from matchers, indicating the error should be propagated. Returning a +// *FatalError indicates that the matcher doesn't process values of the +// supplied type, or otherwise doesn't know how to handle the value. +// +// For example, if GreaterThan(17) returned false for the value "taco" without +// a fatal error, then Not(GreaterThan(17)) would return true. This is +// technically correct, but is surprising and may mask failures where the wrong +// sort of matcher is accidentally used. Instead, GreaterThan(17) can return a +// fatal error, which will be propagated by Not(). +type FatalError struct { + errorText string +} + +// NewFatalError creates a FatalError struct with the supplied error text. +func NewFatalError(s string) *FatalError { + return &FatalError{s} +} + +func (e *FatalError) Error() string { + return e.errorText +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp.go new file mode 100644 index 0000000..1ed63f3 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp.go @@ -0,0 +1,69 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" + "regexp" +) + +// MatchesRegexp returns a matcher that matches strings and byte slices whose +// contents match the supplied regular expression. The semantics are those of +// regexp.Match. In particular, that means the match is not implicitly anchored +// to the ends of the string: MatchesRegexp("bar") will match "foo bar baz". +func MatchesRegexp(pattern string) Matcher { + re, err := regexp.Compile(pattern) + if err != nil { + panic("MatchesRegexp: " + err.Error()) + } + + return &matchesRegexpMatcher{re} +} + +type matchesRegexpMatcher struct { + re *regexp.Regexp +} + +func (m *matchesRegexpMatcher) Description() string { + return fmt.Sprintf("matches regexp \"%s\"", m.re.String()) +} + +func (m *matchesRegexpMatcher) Matches(c interface{}) (err error) { + v := reflect.ValueOf(c) + isString := v.Kind() == reflect.String + isByteSlice := v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Uint8 + + err = errors.New("") + + switch { + case isString: + if m.re.MatchString(v.String()) { + err = nil + } + + case isByteSlice: + if m.re.Match(v.Bytes()) { + err = nil + } + + default: + err = NewFatalError("which is not a string or []byte") + } + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp_test.go new file mode 100644 index 0000000..535038a --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matches_regexp_test.go @@ -0,0 +1,92 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type MatchesRegexpTest struct { +} + +func init() { RegisterTestSuite(&MatchesRegexpTest{}) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *MatchesRegexpTest) Description() { + m := MatchesRegexp("foo.*bar") + ExpectEq("matches regexp \"foo.*bar\"", m.Description()) +} + +func (t *MatchesRegexpTest) InvalidRegexp() { + ExpectThat( + func() { MatchesRegexp("(foo") }, + Panics(HasSubstr("missing closing )"))) +} + +func (t *MatchesRegexpTest) CandidateIsNil() { + m := MatchesRegexp("") + err := m.Matches(nil) + + ExpectThat(err, Error(Equals("which is not a string or []byte"))) + ExpectTrue(isFatal(err)) +} + +func (t *MatchesRegexpTest) CandidateIsInteger() { + m := MatchesRegexp("") + err := m.Matches(17) + + ExpectThat(err, Error(Equals("which is not a string or []byte"))) + ExpectTrue(isFatal(err)) +} + +func (t *MatchesRegexpTest) NonMatchingCandidates() { + m := MatchesRegexp("fo[op]\\s+x") + var err error + + err = m.Matches("fon x") + ExpectThat(err, Error(Equals(""))) + ExpectFalse(isFatal(err)) + + err = m.Matches("fopx") + ExpectThat(err, Error(Equals(""))) + ExpectFalse(isFatal(err)) + + err = m.Matches("fop ") + ExpectThat(err, Error(Equals(""))) + ExpectFalse(isFatal(err)) +} + +func (t *MatchesRegexpTest) MatchingCandidates() { + m := MatchesRegexp("fo[op]\\s+x") + var err error + + err = m.Matches("foo x") + ExpectEq(nil, err) + + err = m.Matches("fop x") + ExpectEq(nil, err) + + err = m.Matches("blah blah foo x blah blah") + ExpectEq(nil, err) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/new_matcher.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/new_matcher.go new file mode 100644 index 0000000..c9d8398 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/new_matcher.go @@ -0,0 +1,43 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +// Create a matcher with the given description and predicate function, which +// will be invoked to handle calls to Matchers. +// +// Using this constructor may be a convenience over defining your own type that +// implements Matcher if you do not need any logic in your Description method. +func NewMatcher( + predicate func(interface{}) error, + description string) Matcher { + return &predicateMatcher{ + predicate: predicate, + description: description, + } +} + +type predicateMatcher struct { + predicate func(interface{}) error + description string +} + +func (pm *predicateMatcher) Matches(c interface{}) error { + return pm.predicate(c) +} + +func (pm *predicateMatcher) Description() string { + return pm.description +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go new file mode 100644 index 0000000..623789f --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go @@ -0,0 +1,53 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" +) + +// Not returns a matcher that inverts the set of values matched by the wrapped +// matcher. It does not transform the result for values for which the wrapped +// matcher returns a fatal error. +func Not(m Matcher) Matcher { + return ¬Matcher{m} +} + +type notMatcher struct { + wrapped Matcher +} + +func (m *notMatcher) Matches(c interface{}) (err error) { + err = m.wrapped.Matches(c) + + // Did the wrapped matcher say yes? + if err == nil { + return errors.New("") + } + + // Did the wrapped matcher return a fatal error? + if _, isFatal := err.(*FatalError); isFatal { + return err + } + + // The wrapped matcher returned a non-fatal error. + return nil +} + +func (m *notMatcher) Description() string { + return fmt.Sprintf("not(%s)", m.wrapped.Description()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/not_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/not_test.go new file mode 100644 index 0000000..7fba6c4 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/not_test.go @@ -0,0 +1,107 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "errors" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "testing" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type fakeMatcher struct { + matchFunc func(interface{}) error + description string +} + +func (m *fakeMatcher) Matches(c interface{}) error { + return m.matchFunc(c) +} + +func (m *fakeMatcher) Description() string { + return m.description +} + +type NotTest struct { +} + +func init() { RegisterTestSuite(&NotTest{}) } +func TestOgletest(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *NotTest) CallsWrapped() { + var suppliedCandidate interface{} + matchFunc := func(c interface{}) error { + suppliedCandidate = c + return nil + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Not(wrapped) + + matcher.Matches(17) + ExpectThat(suppliedCandidate, Equals(17)) +} + +func (t *NotTest) WrappedReturnsTrue() { + matchFunc := func(c interface{}) error { + return nil + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Not(wrapped) + + err := matcher.Matches(0) + ExpectThat(err, Error(Equals(""))) +} + +func (t *NotTest) WrappedReturnsNonFatalError() { + matchFunc := func(c interface{}) error { + return errors.New("taco") + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Not(wrapped) + + err := matcher.Matches(0) + ExpectEq(nil, err) +} + +func (t *NotTest) WrappedReturnsFatalError() { + matchFunc := func(c interface{}) error { + return NewFatalError("taco") + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Not(wrapped) + + err := matcher.Matches(0) + ExpectThat(err, Error(Equals("taco"))) +} + +func (t *NotTest) Description() { + wrapped := &fakeMatcher{nil, "taco"} + matcher := Not(wrapped) + + ExpectEq("not(taco)", matcher.Description()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/panics.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/panics.go new file mode 100644 index 0000000..d2cfc97 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/panics.go @@ -0,0 +1,74 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" +) + +// Panics matches zero-arg functions which, when invoked, panic with an error +// that matches the supplied matcher. +// +// NOTE(jacobsa): This matcher cannot detect the case where the function panics +// using panic(nil), by design of the language. See here for more info: +// +// http://goo.gl/9aIQL +// +func Panics(m Matcher) Matcher { + return &panicsMatcher{m} +} + +type panicsMatcher struct { + wrappedMatcher Matcher +} + +func (m *panicsMatcher) Description() string { + return "panics with: " + m.wrappedMatcher.Description() +} + +func (m *panicsMatcher) Matches(c interface{}) (err error) { + // Make sure c is a zero-arg function. + v := reflect.ValueOf(c) + if v.Kind() != reflect.Func || v.Type().NumIn() != 0 { + err = NewFatalError("which is not a zero-arg function") + return + } + + // Call the function and check its panic error. + defer func() { + if e := recover(); e != nil { + err = m.wrappedMatcher.Matches(e) + + // Set a clearer error message if the matcher said no. + if err != nil { + wrappedClause := "" + if err.Error() != "" { + wrappedClause = ", " + err.Error() + } + + err = errors.New(fmt.Sprintf("which panicked with: %v%s", e, wrappedClause)) + } + } + }() + + v.Call([]reflect.Value{}) + + // If we get here, the function didn't panic. + err = errors.New("which didn't panic") + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/panics_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/panics_test.go new file mode 100644 index 0000000..4978d01 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/panics_test.go @@ -0,0 +1,141 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "errors" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type PanicsTest struct { + matcherCalled bool + suppliedCandidate interface{} + wrappedError error + + matcher Matcher +} + +func init() { RegisterTestSuite(&PanicsTest{}) } + +func (t *PanicsTest) SetUp(i *TestInfo) { + wrapped := &fakeMatcher{ + func(c interface{}) error { + t.matcherCalled = true + t.suppliedCandidate = c + return t.wrappedError + }, + "foo", + } + + t.matcher = Panics(wrapped) +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *PanicsTest) Description() { + ExpectThat(t.matcher.Description(), Equals("panics with: foo")) +} + +func (t *PanicsTest) CandidateIsNil() { + err := t.matcher.Matches(nil) + + ExpectThat(err, Error(Equals("which is not a zero-arg function"))) + ExpectTrue(isFatal(err)) +} + +func (t *PanicsTest) CandidateIsString() { + err := t.matcher.Matches("taco") + + ExpectThat(err, Error(Equals("which is not a zero-arg function"))) + ExpectTrue(isFatal(err)) +} + +func (t *PanicsTest) CandidateTakesArgs() { + err := t.matcher.Matches(func(i int) string { return "" }) + + ExpectThat(err, Error(Equals("which is not a zero-arg function"))) + ExpectTrue(isFatal(err)) +} + +func (t *PanicsTest) CallsFunction() { + callCount := 0 + t.matcher.Matches(func() string { + callCount++ + return "" + }) + + ExpectThat(callCount, Equals(1)) +} + +func (t *PanicsTest) FunctionDoesntPanic() { + err := t.matcher.Matches(func() {}) + + ExpectThat(err, Error(Equals("which didn't panic"))) + ExpectFalse(isFatal(err)) +} + +func (t *PanicsTest) CallsWrappedMatcher() { + expectedErr := 17 + t.wrappedError = errors.New("") + t.matcher.Matches(func() { panic(expectedErr) }) + + ExpectThat(t.suppliedCandidate, Equals(expectedErr)) +} + +func (t *PanicsTest) WrappedReturnsTrue() { + err := t.matcher.Matches(func() { panic("") }) + + ExpectEq(nil, err) +} + +func (t *PanicsTest) WrappedReturnsFatalErrorWithoutText() { + t.wrappedError = NewFatalError("") + err := t.matcher.Matches(func() { panic(17) }) + + ExpectThat(err, Error(Equals("which panicked with: 17"))) + ExpectFalse(isFatal(err)) +} + +func (t *PanicsTest) WrappedReturnsFatalErrorWithText() { + t.wrappedError = NewFatalError("which blah") + err := t.matcher.Matches(func() { panic(17) }) + + ExpectThat(err, Error(Equals("which panicked with: 17, which blah"))) + ExpectFalse(isFatal(err)) +} + +func (t *PanicsTest) WrappedReturnsNonFatalErrorWithoutText() { + t.wrappedError = errors.New("") + err := t.matcher.Matches(func() { panic(17) }) + + ExpectThat(err, Error(Equals("which panicked with: 17"))) + ExpectFalse(isFatal(err)) +} + +func (t *PanicsTest) WrappedReturnsNonFatalErrorWithText() { + t.wrappedError = errors.New("which blah") + err := t.matcher.Matches(func() { panic(17) }) + + ExpectThat(err, Error(Equals("which panicked with: 17, which blah"))) + ExpectFalse(isFatal(err)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee.go new file mode 100644 index 0000000..c5383f2 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee.go @@ -0,0 +1,65 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +import ( + "errors" + "fmt" + "reflect" +) + +// Return a matcher that matches non-nil pointers whose pointee matches the +// wrapped matcher. +func Pointee(m Matcher) Matcher { + return &pointeeMatcher{m} +} + +type pointeeMatcher struct { + wrapped Matcher +} + +func (m *pointeeMatcher) Matches(c interface{}) (err error) { + // Make sure the candidate is of the appropriate type. + cv := reflect.ValueOf(c) + if !cv.IsValid() || cv.Kind() != reflect.Ptr { + return NewFatalError("which is not a pointer") + } + + // Make sure the candidate is non-nil. + if cv.IsNil() { + return NewFatalError("") + } + + // Defer to the wrapped matcher. Fix up empty errors so that failure messages + // are more helpful than just printing a pointer for "Actual". + pointee := cv.Elem().Interface() + err = m.wrapped.Matches(pointee) + if err != nil && err.Error() == "" { + s := fmt.Sprintf("whose pointee is %v", pointee) + + if _, ok := err.(*FatalError); ok { + err = NewFatalError(s) + } else { + err = errors.New(s) + } + } + + return err +} + +func (m *pointeeMatcher) Description() string { + return fmt.Sprintf("pointee(%s)", m.wrapped.Description()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee_test.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee_test.go new file mode 100644 index 0000000..f69969e --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/pointee_test.go @@ -0,0 +1,153 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers_test + +import ( + "errors" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "testing" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type PointeeTest struct{} + +func init() { RegisterTestSuite(&PointeeTest{}) } + +func TestPointee(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *PointeeTest) Description() { + wrapped := &fakeMatcher{nil, "taco"} + matcher := Pointee(wrapped) + + ExpectEq("pointee(taco)", matcher.Description()) +} + +func (t *PointeeTest) CandidateIsNotAPointer() { + matcher := Pointee(HasSubstr("")) + err := matcher.Matches([]byte{}) + + ExpectThat(err, Error(Equals("which is not a pointer"))) + ExpectTrue(isFatal(err)) +} + +func (t *PointeeTest) CandidateIsANilLiteral() { + matcher := Pointee(HasSubstr("")) + err := matcher.Matches(nil) + + ExpectThat(err, Error(Equals("which is not a pointer"))) + ExpectTrue(isFatal(err)) +} + +func (t *PointeeTest) CandidateIsANilPointer() { + matcher := Pointee(HasSubstr("")) + err := matcher.Matches((*int)(nil)) + + ExpectThat(err, Error(Equals(""))) + ExpectTrue(isFatal(err)) +} + +func (t *PointeeTest) CallsWrapped() { + var suppliedCandidate interface{} + matchFunc := func(c interface{}) error { + suppliedCandidate = c + return nil + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Pointee(wrapped) + + someSlice := []byte{} + matcher.Matches(&someSlice) + ExpectThat(suppliedCandidate, IdenticalTo(someSlice)) +} + +func (t *PointeeTest) WrappedReturnsOkay() { + matchFunc := func(c interface{}) error { + return nil + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Pointee(wrapped) + + err := matcher.Matches(new(int)) + ExpectEq(nil, err) +} + +func (t *PointeeTest) WrappedReturnsNonFatalNonEmptyError() { + matchFunc := func(c interface{}) error { + return errors.New("taco") + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Pointee(wrapped) + + i := 17 + err := matcher.Matches(&i) + ExpectFalse(isFatal(err)) + ExpectThat(err, Error(Equals("taco"))) +} + +func (t *PointeeTest) WrappedReturnsNonFatalEmptyError() { + matchFunc := func(c interface{}) error { + return errors.New("") + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Pointee(wrapped) + + i := 17 + err := matcher.Matches(&i) + ExpectFalse(isFatal(err)) + ExpectThat(err, Error(HasSubstr("whose pointee"))) + ExpectThat(err, Error(HasSubstr("17"))) +} + +func (t *PointeeTest) WrappedReturnsFatalNonEmptyError() { + matchFunc := func(c interface{}) error { + return NewFatalError("taco") + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Pointee(wrapped) + + i := 17 + err := matcher.Matches(&i) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(Equals("taco"))) +} + +func (t *PointeeTest) WrappedReturnsFatalEmptyError() { + matchFunc := func(c interface{}) error { + return NewFatalError("") + } + + wrapped := &fakeMatcher{matchFunc, ""} + matcher := Pointee(wrapped) + + i := 17 + err := matcher.Matches(&i) + ExpectTrue(isFatal(err)) + ExpectThat(err, Error(HasSubstr("whose pointee"))) + ExpectThat(err, Error(HasSubstr("17"))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go new file mode 100644 index 0000000..8ea2807 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go @@ -0,0 +1,36 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglematchers + +// transformDescription returns a matcher that is equivalent to the supplied +// one, except that it has the supplied description instead of the one attached +// to the existing matcher. +func transformDescription(m Matcher, newDesc string) Matcher { + return &transformDescriptionMatcher{newDesc, m} +} + +type transformDescriptionMatcher struct { + desc string + wrappedMatcher Matcher +} + +func (m *transformDescriptionMatcher) Description() string { + return m.desc +} + +func (m *transformDescriptionMatcher) Matches(c interface{}) error { + return m.wrappedMatcher.Matches(c) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/LICENSE b/vendor/github.com/smartystreets/assertions/internal/oglemock/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/README.md b/vendor/github.com/smartystreets/assertions/internal/oglemock/README.md new file mode 100644 index 0000000..c5cb5c0 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/README.md @@ -0,0 +1,103 @@ +[![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/oglemock?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/oglemock) + +`oglemock` is a mocking framework for the Go programming language with the +following features: + + * An extensive and extensible set of matchers for expressing call + expectations (provided by the [oglematchers][] package). + + * Clean, readable output that tells you exactly what you need to know. + + * Style and semantics similar to [Google Mock][googlemock] and + [Google JS Test][google-js-test]. + + * Seamless integration with the [ogletest][] unit testing framework. + +It can be integrated into any testing framework (including Go's `testing` +package), but out of the box support is built in to [ogletest][] and that is the +easiest place to use it. + + +Installation +------------ + +First, make sure you have installed Go 1.0.2 or newer. See +[here][golang-install] for instructions. + +Use the following command to install `oglemock` and its dependencies, and to +keep them up to date: + + go get -u github.com/smartystreets/assertions/internal/oglemock + go get -u github.com/smartystreets/assertions/internal/oglemock/createmock + +Those commands will install the `oglemock` package itself, along with the +`createmock` tool that is used to auto-generate mock types. + + +Generating and using mock types +------------------------------- + +Automatically generating a mock implementation of an interface is easy. If you +want to mock interfaces `Bar` and `Baz` from package `foo`, simply run the +following: + + createmock foo Bar Baz + +That will print source code that can be saved to a file and used in your tests. +For example, to create a `mock_io` package containing mock implementations of +`io.Reader` and `io.Writer`: + + mkdir mock_io + createmock io Reader Writer > mock_io/mock_io.go + +The new package will be named `mock_io`, and contain types called `MockReader` +and `MockWriter`, which implement `io.Reader` and `io.Writer` respectively. + +For each generated mock type, there is a corresponding function for creating an +instance of that type given a `Controller` object (see below). For example, to +create a mock reader: + +```go +someController := [...] // See next section. +someReader := mock_io.NewMockReader(someController, "Mock file reader") +``` + +The snippet above creates a mock `io.Reader` that reports failures to +`someController`. The reader can subsequently have expectations set up and be +passed to your code under test that uses an `io.Reader`. + + +Getting ahold of a controller +----------------------------- + +[oglemock.Controller][controller-ref] is used to create mock objects, and to set +up and verify expectations for them. You can create one by calling +`NewController` with an `ErrorReporter`, which is the basic type used to +interface between `oglemock` and the testing framework within which it is being +used. + +If you are using [ogletest][] you don't need to worry about any of this, since +the `TestInfo` struct provided to your test's `SetUp` function already contains +a working `Controller` that you can use to create mock object, and you can use +the built-in `ExpectCall` function for setting expectations. (See the +[ogletest documentation][ogletest-docs] for more info.) Otherwise, you will need +to implement the simple [ErrorReporter interface][reporter-ref] for your test +environment. + + +Documentation +------------- + +For thorough documentation, including information on how to set up expectations, +see [here][oglemock-docs]. + + +[controller-ref]: http://godoc.org/github.com/smartystreets/assertions/internal/oglemock#Controller +[reporter-ref]: http://godoc.org/github.com/smartystreets/assertions/internal/oglemock#ErrorReporter +[golang-install]: http://golang.org/doc/install.html +[google-js-test]: http://code.google.com/p/google-js-test/ +[googlemock]: http://code.google.com/p/googlemock/ +[oglematchers]: https://github.com/smartystreets/assertions/internal/oglematchers +[oglemock-docs]: http://godoc.org/github.com/smartystreets/assertions/internal/oglemock +[ogletest]: https://github.com/smartystreets/assertions/internal/ogletest +[ogletest-docs]: http://godoc.org/github.com/smartystreets/assertions/internal/ogletest diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/action.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/action.go new file mode 100644 index 0000000..9fd40d8 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/action.go @@ -0,0 +1,36 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "reflect" +) + +// Action represents an action to be taken in response to a call to a mock +// method. +type Action interface { + // Set the signature of the function with which this action is being used. + // This must be called before Invoke is called. + SetSignature(signature reflect.Type) error + + // Invoke runs the specified action, given the arguments to the mock method. + // It returns zero or more values that may be treated as the return values of + // the method. If the action doesn't return any values, it may return the nil + // slice. + // + // You must call SetSignature before calling Invoke. + Invoke(methodArgs []interface{}) []interface{} +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/controller.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/controller.go new file mode 100644 index 0000000..93a1d62 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/controller.go @@ -0,0 +1,480 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "errors" + "fmt" + "log" + "math" + "reflect" + "sync" +) + +// PartialExpecation is a function that should be called exactly once with +// expected arguments or matchers in order to set up an expected method call. +// See Controller.ExpectMethodCall below. It returns an expectation that can be +// further modified (e.g. by calling WillOnce). +// +// If the arguments are of the wrong type, the function reports a fatal error +// and returns nil. +type PartialExpecation func(...interface{}) Expectation + +// Controller represents an object that implements the central logic of +// oglemock: recording and verifying expectations, responding to mock method +// calls, and so on. +type Controller interface { + // ExpectCall expresses an expectation that the method of the given name + // should be called on the supplied mock object. It returns a function that + // should be called with the expected arguments, matchers for the arguments, + // or a mix of both. + // + // fileName and lineNumber should indicate the line on which the expectation + // was made, if known. + // + // For example: + // + // mockWriter := [...] + // controller.ExpectCall(mockWriter, "Write", "foo.go", 17)(ElementsAre(0x1)) + // .WillOnce(Return(1, nil)) + // + // If the mock object doesn't have a method of the supplied name, the + // function reports a fatal error and returns nil. + ExpectCall( + o MockObject, + methodName string, + fileName string, + lineNumber int) PartialExpecation + + // Finish causes the controller to check for any unsatisfied expectations, + // and report them as errors if they exist. + // + // The controller may panic if any of its methods (including this one) are + // called after Finish is called. + Finish() + + // HandleMethodCall looks for a registered expectation matching the call of + // the given method on mock object o, invokes the appropriate action (if + // any), and returns the values returned by that action (if any). + // + // If the action returns nothing, the controller returns zero values. If + // there is no matching expectation, the controller reports an error and + // returns zero values. + // + // If the mock object doesn't have a method of the supplied name, the + // arguments are of the wrong type, or the action returns the wrong types, + // the function reports a fatal error. + // + // HandleMethodCall is exported for the sake of mock implementations, and + // should not be used directly. + HandleMethodCall( + o MockObject, + methodName string, + fileName string, + lineNumber int, + args []interface{}) []interface{} +} + +// methodMap represents a map from method name to set of expectations for that +// method. +type methodMap map[string][]*InternalExpectation + +// objectMap represents a map from mock object ID to a methodMap for that object. +type objectMap map[uintptr]methodMap + +// NewController sets up a fresh controller, without any expectations set, and +// configures the controller to use the supplied error reporter. +func NewController(reporter ErrorReporter) Controller { + return &controllerImpl{reporter, sync.RWMutex{}, objectMap{}} +} + +type controllerImpl struct { + reporter ErrorReporter + + mutex sync.RWMutex + expectationsByObject objectMap // Protected by mutex +} + +// Return the list of registered expectations for the named method of the +// supplied object, or an empty slice if none have been registered. When this +// method returns, it is guaranteed that c.expectationsByObject has an entry +// for the object. +// +// c.mutex must be held for reading. +func (c *controllerImpl) getExpectationsLocked( + o MockObject, + methodName string) []*InternalExpectation { + id := o.Oglemock_Id() + + // Look up the mock object. + expectationsByMethod, ok := c.expectationsByObject[id] + if !ok { + expectationsByMethod = methodMap{} + c.expectationsByObject[id] = expectationsByMethod + } + + result, ok := expectationsByMethod[methodName] + if !ok { + return []*InternalExpectation{} + } + + return result +} + +// Add an expectation to the list registered for the named method of the +// supplied mock object. +// +// c.mutex must be held for writing. +func (c *controllerImpl) addExpectationLocked( + o MockObject, + methodName string, + exp *InternalExpectation) { + // Get the existing list. + existing := c.getExpectationsLocked(o, methodName) + + // Store a modified list. + id := o.Oglemock_Id() + c.expectationsByObject[id][methodName] = append(existing, exp) +} + +func (c *controllerImpl) ExpectCall( + o MockObject, + methodName string, + fileName string, + lineNumber int) PartialExpecation { + // Find the signature for the requested method. + ov := reflect.ValueOf(o) + method := ov.MethodByName(methodName) + if method.Kind() == reflect.Invalid { + c.reporter.ReportFatalError( + fileName, + lineNumber, + errors.New("Unknown method: "+methodName)) + return nil + } + + partialAlreadyCalled := false // Protected by c.mutex + return func(args ...interface{}) Expectation { + c.mutex.Lock() + defer c.mutex.Unlock() + + // This function should only be called once. + if partialAlreadyCalled { + c.reporter.ReportFatalError( + fileName, + lineNumber, + errors.New("Partial expectation called more than once.")) + return nil + } + + partialAlreadyCalled = true + + // Make sure that the number of args is legal. Keep in mind that the + // method's type has an extra receiver arg. + if len(args) != method.Type().NumIn() { + c.reporter.ReportFatalError( + fileName, + lineNumber, + errors.New( + fmt.Sprintf( + "Expectation for %s given wrong number of arguments: "+ + "expected %d, got %d.", + methodName, + method.Type().NumIn(), + len(args)))) + return nil + } + + // Create an expectation and insert it into the controller's map. + exp := InternalNewExpectation( + c.reporter, + method.Type(), + args, + fileName, + lineNumber) + + c.addExpectationLocked(o, methodName, exp) + + // Return the expectation to the user. + return exp + } +} + +func (c *controllerImpl) Finish() { + c.mutex.Lock() + defer c.mutex.Unlock() + + // Check whether the minimum cardinality for each registered expectation has + // been satisfied. + for _, expectationsByMethod := range c.expectationsByObject { + for methodName, expectations := range expectationsByMethod { + for _, exp := range expectations { + exp.mutex.Lock() + defer exp.mutex.Unlock() + + minCardinality, _ := computeCardinalityLocked(exp) + if exp.NumMatches < minCardinality { + c.reporter.ReportError( + exp.FileName, + exp.LineNumber, + errors.New( + fmt.Sprintf( + "Unsatisfied expectation; expected %s to be called "+ + "at least %d times; called %d times.", + methodName, + minCardinality, + exp.NumMatches))) + } + } + } + } +} + +// expectationMatches checks the matchers for the expectation against the +// supplied arguments. +func expectationMatches(exp *InternalExpectation, args []interface{}) bool { + matchers := exp.ArgMatchers + if len(args) != len(matchers) { + panic("expectationMatches: len(args)") + } + + // Check each matcher. + for i, matcher := range matchers { + if err := matcher.Matches(args[i]); err != nil { + return false + } + } + + return true +} + +// Return the expectation that matches the supplied arguments. If there is more +// than one such expectation, the one furthest along in the list for the method +// is returned. If there is no such expectation, nil is returned. +// +// c.mutex must be held for reading. +func (c *controllerImpl) chooseExpectationLocked( + o MockObject, + methodName string, + args []interface{}) *InternalExpectation { + // Do we have any expectations for this method? + expectations := c.getExpectationsLocked(o, methodName) + if len(expectations) == 0 { + return nil + } + + for i := len(expectations) - 1; i >= 0; i-- { + if expectationMatches(expectations[i], args) { + return expectations[i] + } + } + + return nil +} + +// makeZeroReturnValues creates a []interface{} containing appropriate zero +// values for returning from the supplied method type. +func makeZeroReturnValues(signature reflect.Type) []interface{} { + result := make([]interface{}, signature.NumOut()) + + for i, _ := range result { + outType := signature.Out(i) + zeroVal := reflect.Zero(outType) + result[i] = zeroVal.Interface() + } + + return result +} + +// computeCardinality decides on the [min, max] range of the number of expected +// matches for the supplied expectations, according to the rules documented in +// expectation.go. +// +// exp.mutex must be held for reading. +func computeCardinalityLocked(exp *InternalExpectation) (min, max uint) { + // Explicit cardinality. + if exp.ExpectedNumMatches >= 0 { + min = uint(exp.ExpectedNumMatches) + max = min + return + } + + // Implicit count based on one-time actions. + if len(exp.OneTimeActions) != 0 { + min = uint(len(exp.OneTimeActions)) + max = min + + // If there is a fallback action, this is only a lower bound. + if exp.FallbackAction != nil { + max = math.MaxUint32 + } + + return + } + + // Implicit lack of restriction based on a fallback action being configured. + if exp.FallbackAction != nil { + min = 0 + max = math.MaxUint32 + return + } + + // Implicit cardinality of one. + min = 1 + max = 1 + return +} + +// chooseAction returns the action that should be invoked for the i'th match to +// the supplied expectation (counting from zero). If the implicit "return zero +// values" action should be used, it returns nil. +// +// exp.mutex must be held for reading. +func chooseActionLocked(i uint, exp *InternalExpectation) Action { + // Exhaust one-time actions first. + if i < uint(len(exp.OneTimeActions)) { + return exp.OneTimeActions[i] + } + + // Fallback action (or nil if none is configured). + return exp.FallbackAction +} + +// Find an action for the method call, updating expectation match state in the +// process. Return either an action that should be invoked or a set of zero +// values to return immediately. +// +// This is split out from HandleMethodCall in order to more easily avoid +// invoking the action with locks held. +func (c *controllerImpl) chooseActionAndUpdateExpectations( + o MockObject, + methodName string, + fileName string, + lineNumber int, + args []interface{}, +) (action Action, zeroVals []interface{}) { + c.mutex.Lock() + defer c.mutex.Unlock() + + // Find the signature for the requested method. + ov := reflect.ValueOf(o) + method := ov.MethodByName(methodName) + if method.Kind() == reflect.Invalid { + c.reporter.ReportFatalError( + fileName, + lineNumber, + errors.New("Unknown method: "+methodName), + ) + + // Should never get here in real code. + log.Println("ReportFatalError unexpectedly returned.") + return + } + + // HACK(jacobsa): Make sure we got the correct number of arguments. This will + // need to be refined when issue #5 (variadic methods) is handled. + if len(args) != method.Type().NumIn() { + c.reporter.ReportFatalError( + fileName, + lineNumber, + errors.New( + fmt.Sprintf( + "Wrong number of arguments: expected %d; got %d", + method.Type().NumIn(), + len(args), + ), + ), + ) + + // Should never get here in real code. + log.Println("ReportFatalError unexpectedly returned.") + return + } + + // Find an expectation matching this call. + expectation := c.chooseExpectationLocked(o, methodName, args) + if expectation == nil { + c.reporter.ReportError( + fileName, + lineNumber, + errors.New( + fmt.Sprintf("Unexpected call to %s with args: %v", methodName, args), + ), + ) + + zeroVals = makeZeroReturnValues(method.Type()) + return + } + + expectation.mutex.Lock() + defer expectation.mutex.Unlock() + + // Increase the number of matches recorded, and check whether we're over the + // number expected. + expectation.NumMatches++ + _, maxCardinality := computeCardinalityLocked(expectation) + if expectation.NumMatches > maxCardinality { + c.reporter.ReportError( + expectation.FileName, + expectation.LineNumber, + errors.New( + fmt.Sprintf( + "Unexpected call to %s: "+ + "expected to be called at most %d times; called %d times.", + methodName, + maxCardinality, + expectation.NumMatches, + ), + ), + ) + + zeroVals = makeZeroReturnValues(method.Type()) + return + } + + // Choose an action to invoke. If there is none, just return zero values. + action = chooseActionLocked(expectation.NumMatches-1, expectation) + if action == nil { + zeroVals = makeZeroReturnValues(method.Type()) + return + } + + // Let the action take over. + return +} + +func (c *controllerImpl) HandleMethodCall( + o MockObject, + methodName string, + fileName string, + lineNumber int, + args []interface{}, +) []interface{} { + // Figure out whether to invoke an action or return zero values. + action, zeroVals := c.chooseActionAndUpdateExpectations( + o, + methodName, + fileName, + lineNumber, + args, + ) + + if action != nil { + return action.Invoke(args) + } + + return zeroVals +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/controller_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/controller_test.go new file mode 100644 index 0000000..be88f58 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/controller_test.go @@ -0,0 +1,1249 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "reflect" +) + +//////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////// + +type errorReport struct { + fileName string + lineNumber int + err error +} + +type fakeErrorReporter struct { + errors []errorReport + fatalErrors []errorReport +} + +func (r *fakeErrorReporter) ReportError(fileName string, lineNumber int, err error) { + report := errorReport{fileName, lineNumber, err} + r.errors = append(r.errors, report) +} + +func (r *fakeErrorReporter) ReportFatalError(fileName string, lineNumber int, err error) { + report := errorReport{fileName, lineNumber, err} + r.fatalErrors = append(r.fatalErrors, report) +} + +type trivialMockObject struct { + id uintptr + desc string +} + +func (o *trivialMockObject) Oglemock_Id() uintptr { + return o.id +} + +func (o *trivialMockObject) Oglemock_Description() string { + return o.desc +} + +// Method being mocked +func (o *trivialMockObject) StringToInt(s string) int { + return 0 +} + +// Method being mocked +func (o *trivialMockObject) TwoIntsToString(i, j int) string { + return "" +} + +type ControllerTest struct { + reporter fakeErrorReporter + controller Controller + + mock1 MockObject + mock2 MockObject +} + +func (t *ControllerTest) SetUp(c *TestInfo) { + t.reporter.errors = make([]errorReport, 0) + t.reporter.fatalErrors = make([]errorReport, 0) + t.controller = NewController(&t.reporter) + + t.mock1 = &trivialMockObject{17, "taco"} + t.mock2 = &trivialMockObject{19, "burrito"} +} + +func init() { RegisterTestSuite(&ControllerTest{}) } + +//////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////// + +func (t *ControllerTest) FinishWithoutAnyEvents() { + t.controller.Finish() + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) HandleCallForUnknownObject() { + p := []byte{255} + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "taco.go", + 112, + []interface{}{p}) + + // The error should be reported immediately. + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("taco.go", t.reporter.errors[0].fileName) + ExpectEq(112, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unexpected"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("[255]"))) + + // Finish should change nothing. + t.controller.Finish() + + ExpectEq(1, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ExpectCallForUnknownMethod() { + ExpectEq( + nil, + t.controller.ExpectCall(t.mock1, "Frobnicate", "burrito.go", 117)) + + // A fatal error should be reported immediately. + AssertEq(0, len(t.reporter.errors)) + AssertEq(1, len(t.reporter.fatalErrors)) + + report := t.reporter.fatalErrors[0] + ExpectEq("burrito.go", report.fileName) + ExpectEq(117, report.lineNumber) + ExpectThat(report.err, Error(HasSubstr("Unknown method"))) + ExpectThat(report.err, Error(HasSubstr("Frobnicate"))) +} + +func (t *ControllerTest) PartialExpectationGivenWrongNumberOfArgs() { + ExpectEq( + nil, + t.controller.ExpectCall(t.mock1, "TwoIntsToString", "burrito.go", 117)( + 17, 19, 23)) + + // A fatal error should be reported immediately. + AssertEq(0, len(t.reporter.errors)) + AssertEq(1, len(t.reporter.fatalErrors)) + + report := t.reporter.fatalErrors[0] + ExpectEq("burrito.go", report.fileName) + ExpectEq(117, report.lineNumber) + ExpectThat(report.err, Error(HasSubstr("TwoIntsToString"))) + ExpectThat(report.err, Error(HasSubstr("arguments"))) + ExpectThat(report.err, Error(HasSubstr("expected 2"))) + ExpectThat(report.err, Error(HasSubstr("got 3"))) +} + +func (t *ControllerTest) PartialExpectationCalledTwice() { + partial := t.controller.ExpectCall(t.mock1, "StringToInt", "burrito.go", 117) + AssertNe(nil, partial("taco")) + ExpectEq(nil, partial("taco")) + + // A fatal error should be reported immediately. + AssertEq(0, len(t.reporter.errors)) + AssertEq(1, len(t.reporter.fatalErrors)) + + report := t.reporter.fatalErrors[0] + ExpectEq("burrito.go", report.fileName) + ExpectEq(117, report.lineNumber) + ExpectThat(report.err, Error(HasSubstr("called more than once"))) +} + +func (t *ControllerTest) HandleMethodCallForUnknownMethod() { + ExpectEq( + nil, + t.controller.HandleMethodCall( + t.mock1, + "Frobnicate", + "burrito.go", + 117, + []interface{}{})) + + // A fatal error should be reported immediately. + AssertEq(0, len(t.reporter.errors)) + AssertEq(1, len(t.reporter.fatalErrors)) + + report := t.reporter.fatalErrors[0] + ExpectEq("burrito.go", report.fileName) + ExpectEq(117, report.lineNumber) + ExpectThat(report.err, Error(HasSubstr("Unknown method"))) + ExpectThat(report.err, Error(HasSubstr("Frobnicate"))) +} + +func (t *ControllerTest) HandleMethodCallGivenWrongNumberOfArgs() { + t.controller.ExpectCall(t.mock1, "TwoIntsToString", "", 0)(17, 19) + + ExpectEq( + nil, + t.controller.HandleMethodCall( + t.mock1, + "TwoIntsToString", + "burrito.go", + 117, + []interface{}{17, 19, 23})) + + // A fatal error should be reported immediately. + AssertEq(0, len(t.reporter.errors)) + AssertEq(1, len(t.reporter.fatalErrors)) + + report := t.reporter.fatalErrors[0] + ExpectEq("burrito.go", report.fileName) + ExpectEq(117, report.lineNumber) + ExpectThat(report.err, Error(HasSubstr("arguments"))) + ExpectThat(report.err, Error(HasSubstr("expected 2"))) + ExpectThat(report.err, Error(HasSubstr("got 3"))) +} + +func (t *ControllerTest) ExpectThenNonMatchingCall() { + // Expectation -- set up a fallback action to make it optional. + partial := t.controller.ExpectCall( + t.mock1, + "TwoIntsToString", + "burrito.go", + 117) + + exp := partial(LessThan(10), Equals(2)) + exp.WillRepeatedly(Return("")) + + // Call + t.controller.HandleMethodCall( + t.mock1, + "TwoIntsToString", + "taco.go", + 112, + []interface{}{8, 1}) + + // The error should be reported immediately. + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("taco.go", t.reporter.errors[0].fileName) + ExpectEq(112, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unexpected"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("TwoIntsToString"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("[8 1]"))) + + // Finish should change nothing. + t.controller.Finish() + + ExpectEq(1, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ExplicitCardinalityNotSatisfied() { + // Expectation -- set up an explicit cardinality of three. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.Times(3) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // The error should not yet be reported. + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) + + // Finish should cause the error to be reported. + t.controller.Finish() + + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unsatisfied"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at least 3 times"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 2 times"))) +} + +func (t *ControllerTest) ImplicitOneTimeActionCountNotSatisfied() { + // Expectation -- add three one-time actions. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + exp.WillOnce(Return(1)) + exp.WillOnce(Return(2)) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // The error should not yet be reported. + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) + + // Finish should cause the error to be reported. + t.controller.Finish() + + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unsatisfied"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at least 3 times"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 2 times"))) +} + +func (t *ControllerTest) ImplicitOneTimeActionLowerBoundNotSatisfied() { + // Expectation -- add three one-time actions and a fallback. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + exp.WillOnce(Return(1)) + exp.WillOnce(Return(2)) + exp.WillRepeatedly(Return(3)) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // The error should not yet be reported. + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) + + // Finish should cause the error to be reported. + t.controller.Finish() + + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unsatisfied"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at least 3 times"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 2 times"))) +} + +func (t *ControllerTest) ImplicitCardinalityOfOneNotSatisfied() { + // Expectation -- add no actions. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + partial(HasSubstr("")) + + // Don't call. + + // The error should not yet be reported. + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) + + // Finish should cause the error to be reported. + t.controller.Finish() + + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unsatisfied"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at least 1 time"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 0 times"))) +} + +func (t *ControllerTest) ExplicitCardinalityOverrun() { + // Expectation -- call times(2). + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.Times(2) + + // Call three times. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // The error should be reported immediately. + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unexpected"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at most 2 times"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 3 times"))) + + // Finish should change nothing. + t.controller.Finish() + + ExpectEq(1, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ImplicitOneTimeActionCountOverrun() { + // Expectation -- add a one-time action. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // The error should be reported immediately. + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unexpected"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at most 1 time"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 2 times"))) + + // Finish should change nothing. + t.controller.Finish() + + ExpectEq(1, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ImplicitCardinalityOfOneOverrun() { + // Expectation -- don't add any actions. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + partial(HasSubstr("")) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // The error should be reported immediately. + AssertEq(1, len(t.reporter.errors)) + AssertEq(0, len(t.reporter.fatalErrors)) + + ExpectEq("burrito.go", t.reporter.errors[0].fileName) + ExpectEq(117, t.reporter.errors[0].lineNumber) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("Unexpected"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("StringToInt"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("at most 1 time"))) + ExpectThat(t.reporter.errors[0].err, Error(HasSubstr("called 2 times"))) + + // Finish should change nothing. + t.controller.Finish() + + ExpectEq(1, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ExplicitCardinalitySatisfied() { + // Expectation -- set up an explicit cardinality of two. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.Times(2) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ImplicitOneTimeActionCountSatisfied() { + // Expectation -- set up two one-time actions. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + exp.WillOnce(Return(1)) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ImplicitOneTimeActionLowerBoundJustSatisfied() { + // Expectation -- set up two one-time actions and a fallback. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + exp.WillOnce(Return(1)) + exp.WillRepeatedly(Return(2)) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ImplicitOneTimeActionLowerBoundMoreThanSatisfied() { + // Expectation -- set up two one-time actions and a fallback. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + exp.WillOnce(Return(1)) + exp.WillRepeatedly(Return(2)) + + // Call four times. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) FallbackActionConfiguredWithZeroCalls() { + // Expectation -- set up a fallback action. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillRepeatedly(Return(0)) + + // Don't call. + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) FallbackActionConfiguredWithMultipleCalls() { + // Expectation -- set up a fallback action. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillRepeatedly(Return(0)) + + // Call twice. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) ImplicitCardinalityOfOneSatisfied() { + // Expectation -- don't add actions. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + partial(HasSubstr("")) + + // Call once. + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + // There should be no errors. + t.controller.Finish() + + ExpectEq(0, len(t.reporter.errors)) + ExpectEq(0, len(t.reporter.fatalErrors)) +} + +func (t *ControllerTest) InvokesOneTimeActions() { + var res []interface{} + + // Expectation -- set up two one-time actions. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + suppliedArg := "" + expectedReturn := 17 + + f := func(s string) int { + suppliedArg = s + return expectedReturn + } + + exp := partial(HasSubstr("")) + exp.WillOnce(Invoke(f)) + exp.WillOnce(Return(1)) + + AssertThat(t.reporter.fatalErrors, ElementsAre()) + + // Call 0 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{"taco"}) + + ExpectEq("taco", suppliedArg) + ExpectThat(res, ElementsAre(IdenticalTo(expectedReturn))) + + // Call 1 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(1)) +} + +func (t *ControllerTest) InvokesFallbackActionAfterOneTimeActions() { + var res []interface{} + + // Expectation -- set up two one-time actions and a fallback. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(0)) + exp.WillOnce(Return(1)) + exp.WillRepeatedly(Return(2)) + + // Call 0 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(0)) + + // Call 1 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(1)) + + // Call 2 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(2)) + + // Call 3 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(2)) +} + +func (t *ControllerTest) InvokesFallbackActionWithoutOneTimeActions() { + var res []interface{} + + // Expectation -- set up only a fallback action. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillRepeatedly(Return(2)) + + // Call 0 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(2)) + + // Call 1 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(2)) + + // Call 2 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(2)) +} + +func (t *ControllerTest) ImplicitActionReturnsZeroInts() { + var res []interface{} + + // Expectation -- set up a cardinality of two. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.Times(2) + + // Call 0 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(reflect.TypeOf(res[0]), Equals(reflect.TypeOf(int(0)))) + ExpectThat(res[0], Equals(0)) + + // Call 1 + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(reflect.TypeOf(res[0]), Equals(reflect.TypeOf(int(0)))) + ExpectThat(res[0], Equals(0)) +} + +func (t *ControllerTest) ImplicitActionReturnsEmptyStrings() { + var res []interface{} + + // Expectation -- set up a cardinality of two. + partial := t.controller.ExpectCall( + t.mock1, + "TwoIntsToString", + "burrito.go", + 117) + + exp := partial(LessThan(100), LessThan(100)) + exp.Times(2) + + // Call 0 + res = t.controller.HandleMethodCall( + t.mock1, + "TwoIntsToString", + "", + 0, + []interface{}{0, 0}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals("")) + + // Call 1 + res = t.controller.HandleMethodCall( + t.mock1, + "TwoIntsToString", + "", + 0, + []interface{}{0, 0}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals("")) +} + +func (t *ControllerTest) ExpectationsAreMatchedLastToFirst() { + var res []interface{} + + // General expectation + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillRepeatedly(Return(17)) + + // More specific expectation + partial = t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp = partial(Equals("taco")) + exp.WillRepeatedly(Return(19)) + + // Call -- the second expectation should match. + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{"taco"}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(19)) + + // Call -- the first expectation should match because the second doesn't. + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{"burrito"}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(17)) +} + +func (t *ControllerTest) ExpectationsAreSegregatedByMockObject() { + var res []interface{} + + // Expectation for mock1 -- return 17. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillRepeatedly(Return(17)) + + // Expectation for mock2 -- return 19. + partial = t.controller.ExpectCall( + t.mock2, + "StringToInt", + "burrito.go", + 117) + + exp = partial(HasSubstr("")) + exp.WillRepeatedly(Return(19)) + + // Call mock1. + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(17)) + + // Call mock2. + res = t.controller.HandleMethodCall( + t.mock2, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(19)) +} + +func (t *ControllerTest) ExpectationsAreSegregatedByMethodName() { + var res []interface{} + + // Expectation for StringToInt + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillRepeatedly(Return(17)) + + // Expectation for TwoIntsToString + partial = t.controller.ExpectCall( + t.mock1, + "TwoIntsToString", + "burrito.go", + 117) + + exp = partial(1, 2) + exp.WillRepeatedly(Return("taco")) + + // Call StringToInt. + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals(17)) + + // Call TwoIntsToString. + res = t.controller.HandleMethodCall( + t.mock1, + "TwoIntsToString", + "", + 0, + []interface{}{1, 2}) + + ExpectThat(len(res), Equals(1)) + ExpectThat(res[0], Equals("taco")) +} + +func (t *ControllerTest) ActionCallsAgainMatchingDifferentExpectation() { + var res []interface{} + + // Expectation for StringToInt + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.WillOnce(Return(17)) + + // Expectation for TwoIntsToString -- pretend we call StringToInt. + partial = t.controller.ExpectCall( + t.mock1, + "TwoIntsToString", + "burrito.go", + 117) + + exp = partial(1, 2) + exp.WillOnce(Invoke(func(int, int) string { + t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "taco.go", + 112, + []interface{}{""}) + + return "queso" + })) + + // Call TwoIntsToString. + res = t.controller.HandleMethodCall( + t.mock1, + "TwoIntsToString", + "", + 0, + []interface{}{1, 2}) + + AssertThat(res, ElementsAre("queso")) + + // Finish. Everything should be satisfied. + t.controller.Finish() + + ExpectThat(t.reporter.errors, ElementsAre()) + ExpectThat(t.reporter.fatalErrors, ElementsAre()) +} + +func (t *ControllerTest) ActionCallsAgainMatchingSameExpectation() { + var res []interface{} + + // Expectation for StringToInt -- should be called twice. The first time it + // should call itself. + partial := t.controller.ExpectCall( + t.mock1, + "StringToInt", + "burrito.go", + 117) + + exp := partial(HasSubstr("")) + exp.Times(2) + exp.WillOnce(Invoke(func(string) int { + subCallRes := t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "taco.go", + 112, + []interface{}{""}) + + return subCallRes[0].(int) + 19 + })) + + exp.WillOnce(Return(17)) + + // Call. + res = t.controller.HandleMethodCall( + t.mock1, + "StringToInt", + "", + 0, + []interface{}{""}) + + AssertThat(res, ElementsAre(17+19)) + + // Finish. Everything should be satisfied. + t.controller.Finish() + + ExpectThat(t.reporter.errors, ElementsAre()) + ExpectThat(t.reporter.fatalErrors, ElementsAre()) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/do_all.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/do_all.go new file mode 100644 index 0000000..c0cd3ff --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/do_all.go @@ -0,0 +1,53 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "fmt" + "reflect" +) + +// Create an Action that invokes the supplied actions one after another. The +// return values from the final action are used; others are ignored. +func DoAll(first Action, others ...Action) Action { + return &doAll{ + wrapped: append([]Action{first}, others...), + } +} + +type doAll struct { + wrapped []Action +} + +func (a *doAll) SetSignature(signature reflect.Type) (err error) { + for i, w := range a.wrapped { + err = w.SetSignature(signature) + if err != nil { + err = fmt.Errorf("Action %v: %v", i, err) + return + } + } + + return +} + +func (a *doAll) Invoke(methodArgs []interface{}) (rets []interface{}) { + for _, w := range a.wrapped { + rets = w.Invoke(methodArgs) + } + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/do_all_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/do_all_test.go new file mode 100644 index 0000000..1dc655c --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/do_all_test.go @@ -0,0 +1,90 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + "reflect" + "testing" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +func TestDoAll(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////// +// Boilerplate +//////////////////////////////////////////////////////////// + +type DoAllTest struct { +} + +func init() { RegisterTestSuite(&DoAllTest{}) } + +//////////////////////////////////////////////////////////// +// Test functions +//////////////////////////////////////////////////////////// + +func (t *DoAllTest) FirstActionDoesntLikeSignature() { + f := func(a int, b string) {} + + a0 := oglemock.Invoke(func() {}) + a1 := oglemock.Invoke(f) + a2 := oglemock.Return() + + err := oglemock.DoAll(a0, a1, a2).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("Action 0"))) + ExpectThat(err, Error(HasSubstr("func()"))) +} + +func (t *DoAllTest) LastActionDoesntLikeSignature() { + f := func(a int, b string) {} + + a0 := oglemock.Invoke(f) + a1 := oglemock.Invoke(f) + a2 := oglemock.Return(17) + + err := oglemock.DoAll(a0, a1, a2).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("Action 2"))) + ExpectThat(err, Error(HasSubstr("1 vals; expected 0"))) +} + +func (t *DoAllTest) SingleAction() { + f := func(a int) string { return "" } + a0 := oglemock.Return("taco") + + action := oglemock.DoAll(a0) + AssertEq(nil, action.SetSignature(reflect.TypeOf(f))) + + rets := action.Invoke([]interface{}{17}) + ExpectThat(rets, ElementsAre("taco")) +} + +func (t *DoAllTest) MultipleActions() { + f := func(a int) string { return "" } + + var saved int + a0 := oglemock.SaveArg(0, &saved) + a1 := oglemock.Return("taco") + + action := oglemock.DoAll(a0, a1) + AssertEq(nil, action.SetSignature(reflect.TypeOf(f))) + + rets := action.Invoke([]interface{}{17}) + ExpectEq(17, saved) + ExpectThat(rets, ElementsAre("taco")) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/error_reporter.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/error_reporter.go new file mode 100644 index 0000000..0c3a65e --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/error_reporter.go @@ -0,0 +1,29 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +// ErrorReporter is an interface that wraps methods for reporting errors that +// should cause test failures. +type ErrorReporter interface { + // Report that some failure (e.g. an unsatisfied expectation) occurred. If + // known, fileName and lineNumber should contain information about where it + // occurred. The test may continue if the test framework supports it. + ReportError(fileName string, lineNumber int, err error) + + // Like ReportError, but the test should be halted immediately. It is assumed + // that this method does not return. + ReportFatalError(fileName string, lineNumber int, err error) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/expectation.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/expectation.go new file mode 100644 index 0000000..d18bfb8 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/expectation.go @@ -0,0 +1,59 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +// Expectation is an expectation for zero or more calls to a mock method with +// particular arguments or sets of arguments. +type Expectation interface { + // Times expresses that a matching method call should happen exactly N times. + // Times must not be called more than once, and must not be called after + // WillOnce or WillRepeatedly. + // + // The full rules for the cardinality of an expectation are as follows: + // + // 1. If an explicit cardinality is set with Times(N), then anything other + // than exactly N matching calls will cause a test failure. + // + // 2. Otherwise, if there are any one-time actions set up, then it is + // expected there will be at least that many matching calls. If there is + // not also a fallback action, then it is expected that there will be + // exactly that many. + // + // 3. Otherwise, if there is a fallback action configured, any number of + // matching calls (including zero) is allowed. + // + // 4. Otherwise, the implicit cardinality is one. + // + Times(n uint) Expectation + + // WillOnce configures a "one-time action". WillOnce can be called zero or + // more times, but must be called after any call to Times and before any call + // to WillRepeatedly. + // + // When matching method calls are made on the mock object, one-time actions + // are invoked one per matching call in the order that they were set up until + // they are exhausted. Afterward the fallback action, if any, will be used. + WillOnce(a Action) Expectation + + // WillRepeatedly configures a "fallback action". WillRepeatedly can be + // called zero or one times, and must not be called before Times or WillOnce. + // + // Once all one-time actions are exhausted (see above), the fallback action + // will be invoked for any further method calls. If WillRepeatedly is not + // called, the fallback action is implicitly an action that returns zero + // values for the method's return values. + WillRepeatedly(a Action) Expectation +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/integration_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/integration_test.go new file mode 100644 index 0000000..6d0f8b1 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/integration_test.go @@ -0,0 +1,129 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + "errors" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock/sample/mock_io" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "path" + "runtime" +) + +//////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////// + +func getLineNumber() int { + _, _, line, _ := runtime.Caller(1) + return line +} + +type IntegrationTest struct { + reporter fakeErrorReporter + controller oglemock.Controller + + reader mock_io.MockReader +} + +func init() { RegisterTestSuite(&IntegrationTest{}) } + +func (t *IntegrationTest) SetUp(c *TestInfo) { + t.reporter.errors = make([]errorReport, 0) + t.reporter.fatalErrors = make([]errorReport, 0) + t.controller = oglemock.NewController(&t.reporter) + + t.reader = mock_io.NewMockReader(t.controller, "") +} + +//////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////// + +func (t *IntegrationTest) UnexpectedCall() { + t.reader.Read([]uint8{1, 2, 3}) + expectedLine := getLineNumber() - 1 + + // An error should have been reported. + AssertEq(1, len(t.reporter.errors), "%v", t.reporter.errors) + AssertEq(0, len(t.reporter.fatalErrors), "%v", t.reporter.fatalErrors) + + r := t.reporter.errors[0] + ExpectEq("integration_test.go", path.Base(r.fileName)) + ExpectEq(expectedLine, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("Unexpected"))) + ExpectThat(r.err, Error(HasSubstr("Read"))) + ExpectThat(r.err, Error(HasSubstr("[1 2 3]"))) +} + +func (t *IntegrationTest) ZeroValues() { + // Make an unexpected call. + n, err := t.reader.Read([]uint8{}) + + // Check the return values. + ExpectEq(0, n) + ExpectEq(nil, err) +} + +func (t *IntegrationTest) ExpectedCalls() { + // Expectations + t.controller.ExpectCall(t.reader, "Read", "", 112)(nil). + WillOnce(oglemock.Return(17, nil)). + WillOnce(oglemock.Return(19, nil)) + + t.controller.ExpectCall(t.reader, "Read", "", 112)(Not(Equals(nil))). + WillOnce(oglemock.Return(23, errors.New("taco"))) + + // Calls + var n int + var err error + + n, err = t.reader.Read(nil) + ExpectEq(17, n) + ExpectEq(nil, err) + + n, err = t.reader.Read([]byte{}) + ExpectEq(23, n) + ExpectThat(err, Error(Equals("taco"))) + + n, err = t.reader.Read(nil) + ExpectEq(19, n) + ExpectEq(nil, err) + + // Errors + AssertEq(0, len(t.reporter.errors), "%v", t.reporter.errors) + AssertEq(0, len(t.reporter.fatalErrors), "%v", t.reporter.fatalErrors) +} + +func (t *IntegrationTest) WrongTypeForReturn() { + t.controller.ExpectCall(t.reader, "Read", "foo.go", 112)(nil). + WillOnce(oglemock.Return(0, errors.New(""))). + WillOnce(oglemock.Return("taco", errors.New(""))) + + // Errors + AssertEq(0, len(t.reporter.errors), "%v", t.reporter.errors) + AssertEq(1, len(t.reporter.fatalErrors), "%v", t.reporter.fatalErrors) + + r := t.reporter.fatalErrors[0] + ExpectEq("foo.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("Return"))) + ExpectThat(r.err, Error(HasSubstr("arg 0"))) + ExpectThat(r.err, Error(HasSubstr("int"))) + ExpectThat(r.err, Error(HasSubstr("string"))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/internal_expectation.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/internal_expectation.go new file mode 100644 index 0000000..68eb2a3 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/internal_expectation.go @@ -0,0 +1,180 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "errors" + "fmt" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + "reflect" + "sync" +) + +// InternalExpectation is exported for purposes of testing only. You should not +// touch it. +// +// InternalExpectation represents an expectation for zero or more calls to a +// mock method, and a set of actions to be taken when those calls are received. +type InternalExpectation struct { + // The signature of the method to which this expectation is bound, for + // checking action types. + methodSignature reflect.Type + + // An error reporter to use for reporting errors in the way that expectations + // are set. + errorReporter ErrorReporter + + // A mutex protecting mutable fields of the struct. + mutex sync.Mutex + + // Matchers that the arguments to the mock method must satisfy in order to + // match this expectation. + ArgMatchers []oglematchers.Matcher + + // The name of the file in which this expectation was expressed. + FileName string + + // The line number at which this expectation was expressed. + LineNumber int + + // The number of times this expectation should be matched, as explicitly + // listed by the user. If there was no explicit number expressed, this is -1. + ExpectedNumMatches int + + // Actions to be taken for the first N calls, one per call in order, where N + // is the length of this slice. + OneTimeActions []Action + + // An action to be taken when the one-time actions have expired, or nil if + // there is no such action. + FallbackAction Action + + // The number of times this expectation has been matched so far. + NumMatches uint +} + +// InternalNewExpectation is exported for purposes of testing only. You should +// not touch it. +func InternalNewExpectation( + reporter ErrorReporter, + methodSignature reflect.Type, + args []interface{}, + fileName string, + lineNumber int) *InternalExpectation { + result := &InternalExpectation{} + + // Store fields that can be stored directly. + result.methodSignature = methodSignature + result.errorReporter = reporter + result.FileName = fileName + result.LineNumber = lineNumber + + // Set up defaults. + result.ExpectedNumMatches = -1 + result.OneTimeActions = make([]Action, 0) + + // Set up the ArgMatchers slice, using Equals(x) for each x that is not a + // matcher itself. + result.ArgMatchers = make([]oglematchers.Matcher, len(args)) + for i, x := range args { + if matcher, ok := x.(oglematchers.Matcher); ok { + result.ArgMatchers[i] = matcher + } else { + result.ArgMatchers[i] = oglematchers.Equals(x) + } + } + + return result +} + +func (e *InternalExpectation) Times(n uint) Expectation { + e.mutex.Lock() + defer e.mutex.Unlock() + + // It is illegal to call this more than once. + if e.ExpectedNumMatches != -1 { + e.reportFatalError("Times called more than once.") + return nil + } + + // It is illegal to call this after any actions are configured. + if len(e.OneTimeActions) != 0 { + e.reportFatalError("Times called after WillOnce.") + return nil + } + + if e.FallbackAction != nil { + e.reportFatalError("Times called after WillRepeatedly.") + return nil + } + + // Make sure the number is reasonable (and will fit in an int). + if n > 1000 { + e.reportFatalError("Expectation.Times: N must be at most 1000") + return nil + } + + e.ExpectedNumMatches = int(n) + return e +} + +func (e *InternalExpectation) WillOnce(a Action) Expectation { + e.mutex.Lock() + defer e.mutex.Unlock() + + // It is illegal to call this after WillRepeatedly. + if e.FallbackAction != nil { + e.reportFatalError("WillOnce called after WillRepeatedly.") + return nil + } + + // Tell the action about the method's signature. + if err := a.SetSignature(e.methodSignature); err != nil { + e.reportFatalError(fmt.Sprintf("WillOnce given invalid action: %v", err)) + return nil + } + + // Store the action. + e.OneTimeActions = append(e.OneTimeActions, a) + + return e +} + +func (e *InternalExpectation) WillRepeatedly(a Action) Expectation { + e.mutex.Lock() + defer e.mutex.Unlock() + + // It is illegal to call this twice. + if e.FallbackAction != nil { + e.reportFatalError("WillRepeatedly called more than once.") + return nil + } + + // Tell the action about the method's signature. + if err := a.SetSignature(e.methodSignature); err != nil { + e.reportFatalError(fmt.Sprintf("WillRepeatedly given invalid action: %v", err)) + return nil + } + + // Store the action. + e.FallbackAction = a + + return e +} + +func (e *InternalExpectation) reportFatalError(errorText string) { + e.errorReporter.ReportFatalError(e.FileName, e.LineNumber, errors.New(errorText)) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/internal_expectation_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/internal_expectation_test.go new file mode 100644 index 0000000..acf17ca --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/internal_expectation_test.go @@ -0,0 +1,265 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "reflect" +) + +//////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////// + +var emptyReturnSig reflect.Type = reflect.TypeOf(func(i int) {}) +var float64ReturnSig reflect.Type = reflect.TypeOf(func(i int) float64 { return 17.0 }) + +type InternalExpectationTest struct { + reporter fakeErrorReporter +} + +func init() { RegisterTestSuite(&InternalExpectationTest{}) } + +func (t *InternalExpectationTest) SetUp(c *TestInfo) { + t.reporter.errors = make([]errorReport, 0) + t.reporter.fatalErrors = make([]errorReport, 0) +} + +func (t *InternalExpectationTest) makeExpectation( + sig reflect.Type, + args []interface{}, + fileName string, + lineNumber int) *InternalExpectation { + return InternalNewExpectation(&t.reporter, sig, args, fileName, lineNumber) +} + +//////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////// + +func (t *InternalExpectationTest) StoresFileNameAndLineNumber() { + args := []interface{}{} + exp := t.makeExpectation(emptyReturnSig, args, "taco", 17) + + ExpectThat(exp.FileName, Equals("taco")) + ExpectThat(exp.LineNumber, Equals(17)) +} + +func (t *InternalExpectationTest) NoArgs() { + args := []interface{}{} + exp := t.makeExpectation(emptyReturnSig, args, "", 0) + + ExpectThat(len(exp.ArgMatchers), Equals(0)) +} + +func (t *InternalExpectationTest) MixOfMatchersAndNonMatchers() { + args := []interface{}{Equals(17), 19, Equals(23)} + exp := t.makeExpectation(emptyReturnSig, args, "", 0) + + // Matcher args + ExpectThat(len(exp.ArgMatchers), Equals(3)) + ExpectThat(exp.ArgMatchers[0], Equals(args[0])) + ExpectThat(exp.ArgMatchers[2], Equals(args[2])) + + // Non-matcher arg + var err error + matcher1 := exp.ArgMatchers[1] + + err = matcher1.Matches(17) + ExpectNe(nil, err) + + err = matcher1.Matches(19) + ExpectEq(nil, err) + + err = matcher1.Matches(23) + ExpectNe(nil, err) +} + +func (t *InternalExpectationTest) NoTimes() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "", 0) + + ExpectThat(exp.ExpectedNumMatches, Equals(-1)) +} + +func (t *InternalExpectationTest) TimesN() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "", 0) + exp.Times(17) + + ExpectThat(exp.ExpectedNumMatches, Equals(17)) +} + +func (t *InternalExpectationTest) NoActions() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "", 0) + + ExpectThat(len(exp.OneTimeActions), Equals(0)) + ExpectThat(exp.FallbackAction, Equals(nil)) +} + +func (t *InternalExpectationTest) WillOnce() { + action0 := Return(17.0) + action1 := Return(19.0) + + exp := t.makeExpectation(float64ReturnSig, []interface{}{}, "", 0) + exp.WillOnce(action0).WillOnce(action1) + + ExpectThat(len(exp.OneTimeActions), Equals(2)) + ExpectThat(exp.OneTimeActions[0], Equals(action0)) + ExpectThat(exp.OneTimeActions[1], Equals(action1)) +} + +func (t *InternalExpectationTest) WillRepeatedly() { + action := Return(17.0) + + exp := t.makeExpectation(float64ReturnSig, []interface{}{}, "", 0) + exp.WillRepeatedly(action) + + ExpectThat(exp.FallbackAction, Equals(action)) +} + +func (t *InternalExpectationTest) BothKindsOfAction() { + action0 := Return(17.0) + action1 := Return(19.0) + action2 := Return(23.0) + + exp := t.makeExpectation(float64ReturnSig, []interface{}{}, "", 0) + exp.WillOnce(action0).WillOnce(action1).WillRepeatedly(action2) + + ExpectThat(len(exp.OneTimeActions), Equals(2)) + ExpectThat(exp.OneTimeActions[0], Equals(action0)) + ExpectThat(exp.OneTimeActions[1], Equals(action1)) + ExpectThat(exp.FallbackAction, Equals(action2)) +} + +func (t *InternalExpectationTest) TimesCalledWithHugeNumber() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "taco.go", 112) + exp.Times(1 << 30) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("Times"))) + ExpectThat(r.err, Error(HasSubstr("N must be at most 1000"))) +} + +func (t *InternalExpectationTest) TimesCalledTwice() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "taco.go", 112) + exp.Times(17) + exp.Times(17) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("Times"))) + ExpectThat(r.err, Error(HasSubstr("more than once"))) +} + +func (t *InternalExpectationTest) TimesCalledAfterWillOnce() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "taco.go", 112) + exp.WillOnce(Return()) + exp.Times(17) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("Times"))) + ExpectThat(r.err, Error(HasSubstr("after WillOnce"))) +} + +func (t *InternalExpectationTest) TimesCalledAfterWillRepeatedly() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "taco.go", 112) + exp.WillRepeatedly(Return()) + exp.Times(17) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("Times"))) + ExpectThat(r.err, Error(HasSubstr("after WillRepeatedly"))) +} + +func (t *InternalExpectationTest) WillOnceCalledAfterWillRepeatedly() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "taco.go", 112) + exp.WillRepeatedly(Return()) + exp.WillOnce(Return()) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("WillOnce"))) + ExpectThat(r.err, Error(HasSubstr("after WillRepeatedly"))) +} + +func (t *InternalExpectationTest) OneTimeActionRejectsSignature() { + exp := t.makeExpectation(float64ReturnSig, []interface{}{}, "taco.go", 112) + exp.WillOnce(Return("taco")) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("arg 0"))) + ExpectThat(r.err, Error(HasSubstr("expected float64"))) + ExpectThat(r.err, Error(HasSubstr("given string"))) +} + +func (t *InternalExpectationTest) WillRepeatedlyCalledTwice() { + exp := t.makeExpectation(emptyReturnSig, []interface{}{}, "taco.go", 112) + exp.WillRepeatedly(Return()) + exp.WillRepeatedly(Return()) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("WillRepeatedly"))) + ExpectThat(r.err, Error(HasSubstr("once"))) +} + +func (t *InternalExpectationTest) FallbackActionRejectsSignature() { + exp := t.makeExpectation(float64ReturnSig, []interface{}{}, "taco.go", 112) + exp.WillRepeatedly(Return("taco")) + + AssertEq(1, len(t.reporter.fatalErrors)) + AssertEq(0, len(t.reporter.errors)) + + r := t.reporter.fatalErrors[0] + ExpectEq("taco.go", r.fileName) + ExpectEq(112, r.lineNumber) + ExpectThat(r.err, Error(HasSubstr("arg 0"))) + ExpectThat(r.err, Error(HasSubstr("expected float64"))) + ExpectThat(r.err, Error(HasSubstr("given string"))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/invoke.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/invoke.go new file mode 100644 index 0000000..07630cb --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/invoke.go @@ -0,0 +1,73 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "errors" + "fmt" + "reflect" +) + +// Create an Action that invokes the supplied function, returning whatever it +// returns. The signature of the function must match that of the mocked method +// exactly. +func Invoke(f interface{}) Action { + // Make sure f is a function. + fv := reflect.ValueOf(f) + fk := fv.Kind() + + if fk != reflect.Func { + desc := "" + if fk != reflect.Invalid { + desc = fv.Type().String() + } + + panic(fmt.Sprintf("Invoke: expected function, got %s", desc)) + } + + return &invokeAction{fv} +} + +type invokeAction struct { + f reflect.Value +} + +func (a *invokeAction) SetSignature(signature reflect.Type) error { + // The signature must match exactly. + ft := a.f.Type() + if ft != signature { + return errors.New(fmt.Sprintf("Invoke: expected %v, got %v", signature, ft)) + } + + return nil +} + +func (a *invokeAction) Invoke(vals []interface{}) []interface{} { + // Create a slice of args for the function. + in := make([]reflect.Value, len(vals)) + for i, x := range vals { + in[i] = reflect.ValueOf(x) + } + + // Call the function and return its return values. + out := a.f.Call(in) + result := make([]interface{}, len(out)) + for i, v := range out { + result[i] = v.Interface() + } + + return result +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/invoke_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/invoke_test.go new file mode 100644 index 0000000..a498713 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/invoke_test.go @@ -0,0 +1,110 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "reflect" +) + +//////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////// + +type InvokeTest struct { +} + +func init() { RegisterTestSuite(&InvokeTest{}) } + +//////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////// + +func (t *InvokeTest) ArgumentIsNil() { + f := func() { oglemock.Invoke(nil) } + ExpectThat(f, Panics(MatchesRegexp("Invoke.*function.*"))) +} + +func (t *InvokeTest) ArgumentIsInt() { + f := func() { oglemock.Invoke(17) } + ExpectThat(f, Panics(MatchesRegexp("Invoke.*function.*int"))) +} + +func (t *InvokeTest) FunctionHasOneWrongInputType() { + f := func(a int, b int32, c string) {} + g := func(a int, b int, c string) {} + + err := oglemock.Invoke(f).SetSignature(reflect.TypeOf(g)) + ExpectThat(err, Error(HasSubstr("func(int, int32, string)"))) + ExpectThat(err, Error(HasSubstr("func(int, int, string)"))) +} + +func (t *InvokeTest) FunctionHasOneWrongOutputType() { + f := func() (int32, string) { return 0, "" } + g := func() (int, string) { return 0, "" } + + err := oglemock.Invoke(f).SetSignature(reflect.TypeOf(g)) + ExpectThat(err, Error(HasSubstr("func() (int32, string)"))) + ExpectThat(err, Error(HasSubstr("func() (int, string)"))) +} + +func (t *InvokeTest) CallsFunction() { + var actualArg0, actualArg1 interface{} + + f := func(a uintptr, b int8) { + actualArg0 = a + actualArg1 = b + } + + a := oglemock.Invoke(f) + + // Set signature. + AssertEq(nil, a.SetSignature(reflect.TypeOf(f))) + + // Call the action. + expectedArg0 := uintptr(17) + expectedArg1 := int8(-7) + + a.Invoke([]interface{}{expectedArg0, expectedArg1}) + + ExpectThat(actualArg0, IdenticalTo(expectedArg0)) + ExpectThat(actualArg1, IdenticalTo(expectedArg1)) +} + +func (t *InvokeTest) ReturnsFunctionResult() { + expectedReturn0 := int16(3) + expectedReturn1 := "taco" + + f := func() (int16, string) { + return expectedReturn0, expectedReturn1 + } + + a := oglemock.Invoke(f) + + // Set signature. + AssertEq(nil, a.SetSignature(reflect.TypeOf(f))) + + // Call the action. + res := a.Invoke([]interface{}{}) + + ExpectThat( + res, + ElementsAre( + IdenticalTo(expectedReturn0), + IdenticalTo(expectedReturn1))) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/mock_object.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/mock_object.go new file mode 100644 index 0000000..de995ef --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/mock_object.go @@ -0,0 +1,30 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +// MockObject is an interface that mock object implementations must conform to +// in order to register expectations with and hand off calls to a +// MockController. Users should not interact with this interface directly. +type MockObject interface { + // Oglemock_Id returns an identifier for the mock object that is guaranteed + // to be unique within the process at least until the mock object is garbage + // collected. + Oglemock_Id() uintptr + + // Oglemock_Description returns a description of the mock object that may be + // helpful in test failure messages. + Oglemock_Description() string +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/return.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/return.go new file mode 100644 index 0000000..c66d248 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/return.go @@ -0,0 +1,251 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "errors" + "fmt" + "math" + "reflect" +) + +var intType = reflect.TypeOf(int(0)) +var float64Type = reflect.TypeOf(float64(0)) +var complex128Type = reflect.TypeOf(complex128(0)) + +// Return creates an Action that returns the values passed to Return as +// arguments, after suitable legal type conversions. The following rules apply. +// Given an argument x to Return and a corresponding type T in the method's +// signature, at least one of the following must hold: +// +// * x is assignable to T. (See "Assignability" in the language spec.) Note +// that this in particular applies that x may be a type that implements an +// interface T. It also implies that the nil literal can be used if T is a +// pointer, function, interface, slice, channel, or map type. +// +// * T is any numeric type, and x is an int that is in-range for that type. +// This facilities using raw integer constants: Return(17). +// +// * T is a floating-point or complex number type, and x is a float64. This +// facilities using raw floating-point constants: Return(17.5). +// +// * T is a complex number type, and x is a complex128. This facilities using +// raw complex constants: Return(17+2i). +// +func Return(vals ...interface{}) Action { + return &returnAction{vals, nil} +} + +type returnAction struct { + returnVals []interface{} + signature reflect.Type +} + +func (a *returnAction) Invoke(vals []interface{}) []interface{} { + if a.signature == nil { + panic("You must first call SetSignature with a valid signature.") + } + + res, err := a.buildInvokeResult(a.signature) + if err != nil { + panic(err) + } + + return res +} + +func (a *returnAction) SetSignature(signature reflect.Type) error { + if _, err := a.buildInvokeResult(signature); err != nil { + return err + } + + a.signature = signature + return nil +} + +// A version of Invoke that does error checking, used by both public methods. +func (a *returnAction) buildInvokeResult( + sig reflect.Type) (res []interface{}, err error) { + // Check the length of the return value. + numOut := sig.NumOut() + numVals := len(a.returnVals) + + if numOut != numVals { + err = errors.New( + fmt.Sprintf("Return given %d vals; expected %d.", numVals, numOut)) + return + } + + // Attempt to coerce each return value. + res = make([]interface{}, numOut) + + for i, val := range a.returnVals { + resType := sig.Out(i) + res[i], err = a.coerce(val, resType) + + if err != nil { + res = nil + err = errors.New(fmt.Sprintf("Return: arg %d: %v", i, err)) + return + } + } + + return +} + +func (a *returnAction) coerce(x interface{}, t reflect.Type) (interface{}, error) { + xv := reflect.ValueOf(x) + rv := reflect.New(t).Elem() + + // Special case: the language spec says that the predeclared identifier nil + // is assignable to pointers, functions, interface, slices, channels, and map + // types. However, reflect.ValueOf(nil) returns an invalid value that will + // not cooperate below. So handle invalid values here, assuming that they + // resulted from Return(nil). + if !xv.IsValid() { + switch t.Kind() { + case reflect.Ptr, reflect.Func, reflect.Interface, reflect.Chan, reflect.Slice, reflect.Map, reflect.UnsafePointer: + return rv.Interface(), nil + } + + return nil, errors.New(fmt.Sprintf("expected %v, given ", t)) + } + + // If x is assignable to type t, let the reflect package do the heavy + // lifting. + if reflect.TypeOf(x).AssignableTo(t) { + rv.Set(xv) + return rv.Interface(), nil + } + + // Handle numeric types as described in the documentation on Return. + switch { + case xv.Type() == intType && a.isNumeric(t): + return a.coerceInt(xv.Int(), t) + + case xv.Type() == float64Type && (a.isFloatingPoint(t) || a.isComplex(t)): + return a.coerceFloat(xv.Float(), t) + + case xv.Type() == complex128Type && a.isComplex(t): + return a.coerceComplex(xv.Complex(), t) + } + + // The value wasn't of a legal type. + return nil, errors.New(fmt.Sprintf("expected %v, given %v", t, xv.Type())) +} + +func (a *returnAction) isNumeric(t reflect.Type) bool { + return (t.Kind() >= reflect.Int && t.Kind() <= reflect.Uint64) || + a.isFloatingPoint(t) || + a.isComplex(t) +} + +func (a *returnAction) isFloatingPoint(t reflect.Type) bool { + return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64 +} + +func (a *returnAction) isComplex(t reflect.Type) bool { + return t.Kind() == reflect.Complex64 || t.Kind() == reflect.Complex128 +} + +func (a *returnAction) coerceInt(x int64, t reflect.Type) (interface{}, error) { + k := t.Kind() + + // Floating point and complex numbers: promote appropriately. + if a.isFloatingPoint(t) || a.isComplex(t) { + return a.coerceFloat(float64(x), t) + } + + // Integers: range check. + var min, max int64 + unsigned := false + + switch k { + case reflect.Int8: + min = math.MinInt8 + max = math.MaxInt8 + + case reflect.Int16: + min = math.MinInt16 + max = math.MaxInt16 + + case reflect.Int32: + min = math.MinInt32 + max = math.MaxInt32 + + case reflect.Int64: + min = math.MinInt64 + max = math.MaxInt64 + + case reflect.Uint: + unsigned = true + min = 0 + max = math.MaxUint32 + + case reflect.Uint8: + unsigned = true + min = 0 + max = math.MaxUint8 + + case reflect.Uint16: + unsigned = true + min = 0 + max = math.MaxUint16 + + case reflect.Uint32: + unsigned = true + min = 0 + max = math.MaxUint32 + + case reflect.Uint64: + unsigned = true + min = 0 + max = math.MaxInt64 + + default: + panic(fmt.Sprintf("Unexpected type: %v", t)) + } + + if x < min || x > max { + return nil, errors.New("int value out of range") + } + + rv := reflect.New(t).Elem() + if unsigned { + rv.SetUint(uint64(x)) + } else { + rv.SetInt(x) + } + + return rv.Interface(), nil +} + +func (a *returnAction) coerceFloat(x float64, t reflect.Type) (interface{}, error) { + // Promote complex numbers. + if a.isComplex(t) { + return a.coerceComplex(complex(x, 0), t) + } + + rv := reflect.New(t).Elem() + rv.SetFloat(x) + return rv.Interface(), nil +} + +func (a *returnAction) coerceComplex(x complex128, t reflect.Type) (interface{}, error) { + rv := reflect.New(t).Elem() + rv.SetComplex(x) + return rv.Interface(), nil +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/return_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/return_test.go new file mode 100644 index 0000000..a93371a --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/return_test.go @@ -0,0 +1,978 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + "bytes" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "io" + "math" + "reflect" + "testing" + "unsafe" +) + +//////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////// + +var someInt int = 17 + +type ReturnTest struct { +} + +func init() { RegisterTestSuite(&ReturnTest{}) } +func TestOgletest(t *testing.T) { RunTests(t) } + +type returnTestCase struct { + suppliedVal interface{} + expectedVal interface{} + expectedSetSignatureErrorSubstring string +} + +func (t *ReturnTest) runTestCases(signature reflect.Type, cases []returnTestCase) { + for i, c := range cases { + a := oglemock.Return(c.suppliedVal) + + // SetSignature + err := a.SetSignature(signature) + if c.expectedSetSignatureErrorSubstring == "" { + ExpectEq(nil, err, "Test case %d: %v", i, c) + + if err != nil { + continue + } + } else { + ExpectThat(err, Error(HasSubstr(c.expectedSetSignatureErrorSubstring)), + "Test case %d: %v", i, c) + continue + } + + // Invoke + res := a.Invoke([]interface{}{}) + AssertThat(res, ElementsAre(Any())) + ExpectThat(res[0], IdenticalTo(c.expectedVal), "Test case %d: %v", i, c) + } +} + +//////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////// + +func (t *ReturnTest) SetSignatureNotCalled() { + a := oglemock.Return() + f := func() { a.Invoke([]interface{}{}) } + ExpectThat(f, Panics(MatchesRegexp("first call SetSignature"))) +} + +func (t *ReturnTest) NoReturnValues() { + sig := reflect.TypeOf(func() {}) + var a oglemock.Action + var err error + var vals []interface{} + + // No values. + a = oglemock.Return() + err = a.SetSignature(sig) + AssertEq(nil, err) + + vals = a.Invoke([]interface{}{}) + ExpectThat(vals, ElementsAre()) + + // One value. + a = oglemock.Return(17) + err = a.SetSignature(sig) + ExpectThat(err, Error(HasSubstr("given 1 val"))) + ExpectThat(err, Error(HasSubstr("expected 0"))) + + // Two values. + a = oglemock.Return(17, 19) + err = a.SetSignature(sig) + ExpectThat(err, Error(HasSubstr("given 2 vals"))) + ExpectThat(err, Error(HasSubstr("expected 0"))) +} + +func (t *ReturnTest) MultipleReturnValues() { + sig := reflect.TypeOf(func() (int, string) { return 0, "" }) + var a oglemock.Action + var err error + var vals []interface{} + + // No values. + a = oglemock.Return() + err = a.SetSignature(sig) + ExpectThat(err, Error(HasSubstr("given 0 vals"))) + ExpectThat(err, Error(HasSubstr("expected 2"))) + + // One value. + a = oglemock.Return(17) + err = a.SetSignature(sig) + ExpectThat(err, Error(HasSubstr("given 1 val"))) + ExpectThat(err, Error(HasSubstr("expected 2"))) + + // Two values. + a = oglemock.Return(17, "taco") + err = a.SetSignature(sig) + AssertEq(nil, err) + + vals = a.Invoke([]interface{}{}) + ExpectThat(vals, ElementsAre(IdenticalTo(int(17)), "taco")) +} + +func (t *ReturnTest) Bool() { + sig := reflect.TypeOf(func() bool { return false }) + cases := []returnTestCase{ + // Identical types. + {bool(true), bool(true), ""}, + {bool(false), bool(false), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Int() { + sig := reflect.TypeOf(func() int { return 0 }) + cases := []returnTestCase{ + // Identical types. + {int(math.MinInt32), int(math.MinInt32), ""}, + {int(math.MaxInt32), int(math.MaxInt32), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Int8() { + sig := reflect.TypeOf(func() int8 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {int8(math.MinInt8), int8(math.MinInt8), ""}, + {int8(math.MaxInt8), int8(math.MaxInt8), ""}, + + // In-range ints. + {int(math.MinInt8), int8(math.MinInt8), ""}, + {int(math.MaxInt8), int8(math.MaxInt8), ""}, + + // Out of range ints. + {int(math.MinInt8 - 1), nil, "out of range"}, + {int(math.MaxInt8 + 1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Int16() { + sig := reflect.TypeOf(func() int16 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {int16(math.MinInt16), int16(math.MinInt16), ""}, + {int16(math.MaxInt16), int16(math.MaxInt16), ""}, + + // In-range ints. + {int(math.MinInt16), int16(math.MinInt16), ""}, + {int(math.MaxInt16), int16(math.MaxInt16), ""}, + + // Out of range ints. + {int(math.MinInt16 - 1), nil, "out of range"}, + {int(math.MaxInt16 + 1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int8(1), nil, "given int8"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Int32() { + sig := reflect.TypeOf(func() int32 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {int32(math.MinInt32), int32(math.MinInt32), ""}, + {int32(math.MaxInt32), int32(math.MaxInt32), ""}, + + // Aliased version of type. + {rune(17), int32(17), ""}, + + // In-range ints. + {int(math.MinInt32), int32(math.MinInt32), ""}, + {int(math.MaxInt32), int32(math.MaxInt32), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Rune() { + sig := reflect.TypeOf(func() rune { return 0 }) + cases := []returnTestCase{ + // Identical types. + {rune(math.MinInt32), rune(math.MinInt32), ""}, + {rune(math.MaxInt32), rune(math.MaxInt32), ""}, + + // Aliased version of type. + {int32(17), rune(17), ""}, + + // In-range ints. + {int(math.MinInt32), rune(math.MinInt32), ""}, + {int(math.MaxInt32), rune(math.MaxInt32), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Int64() { + sig := reflect.TypeOf(func() int64 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {int64(math.MinInt64), int64(math.MinInt64), ""}, + {int64(math.MaxInt64), int64(math.MaxInt64), ""}, + + // In-range ints. + {int(math.MinInt32), int64(math.MinInt32), ""}, + {int(math.MaxInt32), int64(math.MaxInt32), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Uint() { + sig := reflect.TypeOf(func() uint { return 0 }) + cases := []returnTestCase{ + // Identical types. + {uint(0), uint(0), ""}, + {uint(math.MaxUint32), uint(math.MaxUint32), ""}, + + // In-range ints. + {int(0), uint(0), ""}, + {int(math.MaxInt32), uint(math.MaxInt32), ""}, + + // Out of range ints. + {int(-1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Uint8() { + sig := reflect.TypeOf(func() uint8 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {uint8(0), uint8(0), ""}, + {uint8(math.MaxUint8), uint8(math.MaxUint8), ""}, + + // Aliased version of type. + {byte(17), uint8(17), ""}, + + // In-range ints. + {int(0), uint8(0), ""}, + {int(math.MaxUint8), uint8(math.MaxUint8), ""}, + + // Out of range ints. + {int(-1), nil, "out of range"}, + {int(math.MaxUint8 + 1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Byte() { + sig := reflect.TypeOf(func() byte { return 0 }) + cases := []returnTestCase{ + // Identical types. + {byte(0), byte(0), ""}, + {byte(math.MaxUint8), byte(math.MaxUint8), ""}, + + // Aliased version of type. + {uint8(17), byte(17), ""}, + + // In-range ints. + {int(0), byte(0), ""}, + {int(math.MaxUint8), byte(math.MaxUint8), ""}, + + // Out of range ints. + {int(-1), nil, "out of range"}, + {int(math.MaxUint8 + 1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Uint16() { + sig := reflect.TypeOf(func() uint16 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {uint16(0), uint16(0), ""}, + {uint16(math.MaxUint16), uint16(math.MaxUint16), ""}, + + // In-range ints. + {int(0), uint16(0), ""}, + {int(math.MaxUint16), uint16(math.MaxUint16), ""}, + + // Out of range ints. + {int(-1), nil, "out of range"}, + {int(math.MaxUint16 + 1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Uint32() { + sig := reflect.TypeOf(func() uint32 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {uint32(0), uint32(0), ""}, + {uint32(math.MaxUint32), uint32(math.MaxUint32), ""}, + + // In-range ints. + {int(0), uint32(0), ""}, + {int(math.MaxInt32), uint32(math.MaxInt32), ""}, + + // Out of range ints. + {int(-1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Uint64() { + sig := reflect.TypeOf(func() uint64 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {uint64(0), uint64(0), ""}, + {uint64(math.MaxUint64), uint64(math.MaxUint64), ""}, + + // In-range ints. + {int(0), uint64(0), ""}, + {int(math.MaxInt32), uint64(math.MaxInt32), ""}, + + // Out of range ints. + {int(-1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Uintptr() { + sig := reflect.TypeOf(func() uintptr { return 0 }) + cases := []returnTestCase{ + // Identical types. + {uintptr(17), uintptr(17), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Float32() { + sig := reflect.TypeOf(func() float32 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {float32(-17.5), float32(-17.5), ""}, + {float32(17.5), float32(17.5), ""}, + + // In-range ints. + {int(-17), float32(-17), ""}, + {int(17), float32(17), ""}, + + // Float64s + {float64(-17.5), float32(-17.5), ""}, + {float64(17.5), float32(17.5), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Float64() { + sig := reflect.TypeOf(func() float64 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {float64(-17.5), float64(-17.5), ""}, + {float64(17.5), float64(17.5), ""}, + + // In-range ints. + {int(-17), float64(-17), ""}, + {int(17), float64(17), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float32(1), nil, "given float32"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Complex64() { + sig := reflect.TypeOf(func() complex64 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {complex64(-17.5 - 1i), complex64(-17.5 - 1i), ""}, + {complex64(17.5 + 1i), complex64(17.5 + 1i), ""}, + + // In-range ints. + {int(-17), complex64(-17), ""}, + {int(17), complex64(17), ""}, + + // Float64s + {float64(-17.5), complex64(-17.5), ""}, + {float64(17.5), complex64(17.5), ""}, + + // Complex128s + {complex128(-17.5 - 1i), complex64(-17.5 - 1i), ""}, + {complex128(17.5 + 1i), complex64(17.5 + 1i), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float32(1), nil, "given float32"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Complex128() { + sig := reflect.TypeOf(func() complex128 { return 0 }) + cases := []returnTestCase{ + // Identical types. + {complex128(-17.5 - 1i), complex128(-17.5 - 1i), ""}, + {complex128(17.5 + 1i), complex128(17.5 + 1i), ""}, + + // In-range ints. + {int(-17), complex128(-17), ""}, + {int(17), complex128(17), ""}, + + // Float64s + {float64(-17.5), complex128(-17.5), ""}, + {float64(17.5), complex128(17.5), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float32(1), nil, "given float32"}, + {complex64(1), nil, "given complex64"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) ArrayOfInt() { + type namedElemType int + + sig := reflect.TypeOf(func() [2]int { return [2]int{0, 0} }) + cases := []returnTestCase{ + // Identical types. + {[2]int{19, 23}, [2]int{19, 23}, ""}, + + // Wrong length. + {[1]int{17}, nil, "given [1]int"}, + + // Wrong element types. + {[2]namedElemType{19, 23}, nil, "given [2]oglemock_test.namedElemType"}, + {[2]string{"", ""}, nil, "given [2]string"}, + + // Wrong types. + {nil, nil, "given "}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) ChanOfInt() { + type namedElemType int + someChan := make(chan int) + + sig := reflect.TypeOf(func() chan int { return nil }) + cases := []returnTestCase{ + // Identical types. + {someChan, someChan, ""}, + + // Nil values. + {(interface{})(nil), (chan int)(nil), ""}, + {(chan int)(nil), (chan int)(nil), ""}, + + // Wrong element types. + {make(chan string), nil, "given chan string"}, + {make(chan namedElemType), nil, "given chan oglemock_test.namedElemType"}, + + // Wrong direction + {(<-chan int)(someChan), nil, "given <-chan int"}, + {(chan<- int)(someChan), nil, "given chan<- int"}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) SendChanOfInt() { + type namedElemType int + + someChan := make(chan<- int) + someBidirectionalChannel := make(chan int) + + sig := reflect.TypeOf(func() chan<- int { return nil }) + cases := []returnTestCase{ + // Identical types. + {someChan, someChan, ""}, + + // Nil values. + {(interface{})(nil), (chan<- int)(nil), ""}, + {(chan int)(nil), (chan<- int)(nil), ""}, + + // Bidirectional channel + {someBidirectionalChannel, (chan<- int)(someBidirectionalChannel), ""}, + + // Wrong direction + {(<-chan int)(someBidirectionalChannel), nil, "given <-chan int"}, + + // Wrong element types. + {make(chan string), nil, "given chan string"}, + {make(chan namedElemType), nil, "given chan oglemock_test.namedElemType"}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) RecvChanOfInt() { + type namedElemType int + + someChan := make(<-chan int) + someBidirectionalChannel := make(chan int) + + sig := reflect.TypeOf(func() <-chan int { return nil }) + cases := []returnTestCase{ + // Identical types. + {someChan, someChan, ""}, + + // Nil values. + {(interface{})(nil), (<-chan int)(nil), ""}, + {(chan int)(nil), (<-chan int)(nil), ""}, + + // Bidirectional channel + {someBidirectionalChannel, (<-chan int)(someBidirectionalChannel), ""}, + + // Wrong direction + {(chan<- int)(someBidirectionalChannel), nil, "given chan<- int"}, + + // Wrong element types. + {make(chan string), nil, "given chan string"}, + {make(chan namedElemType), nil, "given chan oglemock_test.namedElemType"}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Func() { + someFunc := func(string) int { return 0 } + + sig := reflect.TypeOf(func() func(string) int { return nil }) + cases := []returnTestCase{ + // Identical types. + {someFunc, someFunc, ""}, + + // Nil values. + {(interface{})(nil), (func(string) int)(nil), ""}, + {(func(string) int)(nil), (func(string) int)(nil), ""}, + + // Wrong parameter and return types. + {func(int) int { return 0 }, nil, "given func(int) int"}, + {func(string) string { return "" }, nil, "given func(string) string"}, + + // Wrong types. + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {(chan int)(nil), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Interface() { + sig := reflect.TypeOf(func() io.Reader { return nil }) + + someBuffer := new(bytes.Buffer) + + cases := []returnTestCase{ + // Type that implements interface. + {someBuffer, someBuffer, ""}, + + // Nil value. + {(interface{})(nil), (interface{})(nil), ""}, + + // Non-implementing types. + {(chan int)(nil), nil, "given chan int"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) MapFromStringToInt() { + type namedElemType string + + someMap := make(map[string]int) + + sig := reflect.TypeOf(func() map[string]int { return nil }) + cases := []returnTestCase{ + // Identical types. + {someMap, someMap, ""}, + + // Nil values. + {(interface{})(nil), (map[string]int)(nil), ""}, + {(map[string]int)(nil), (map[string]int)(nil), ""}, + + // Wrong element types. + {make(map[int]int), nil, "given map[int]int"}, + {make(map[namedElemType]int), nil, "given map[oglemock_test.namedElemType]int"}, + {make(map[string]string), nil, "given map[string]string"}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) PointerToString() { + type namedElemType string + + someStr := "" + + sig := reflect.TypeOf(func() *string { return nil }) + cases := []returnTestCase{ + // Identical types. + {(*string)(&someStr), (*string)(&someStr), ""}, + + // Nil values. + {(interface{})(nil), (*string)(nil), ""}, + {(*string)(nil), (*string)(nil), ""}, + + // Wrong element types. + {&someInt, nil, "given *int"}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {unsafe.Pointer(&someStr), nil, "given unsafe.Pointer"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) SliceOfInts() { + type namedElemType int + + someSlice := make([]int, 1) + + sig := reflect.TypeOf(func() []int { return nil }) + cases := []returnTestCase{ + // Identical types. + {someSlice, someSlice, ""}, + + // Nil values. + {(interface{})(nil), ([]int)(nil), ""}, + {([]int)(nil), ([]int)(nil), ""}, + + // Wrong element types. + {make([]string, 1), nil, "given []string"}, + {make([]namedElemType, 1), nil, "given []oglemock_test.namedElemType"}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) String() { + sig := reflect.TypeOf(func() string { return "" }) + cases := []returnTestCase{ + // Identical types. + {string(""), string(""), ""}, + {string("taco"), string("taco"), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) Struct() { + type myStruct struct { + a int + } + + type otherStruct struct{} + + sig := reflect.TypeOf(func() myStruct { return myStruct{0} }) + cases := []returnTestCase{ + // Identical types. + {myStruct{17}, myStruct{17}, ""}, + + // Wrong field types. + {otherStruct{}, nil, "given oglemock_test.otherStruct"}, + + // Wrong types. + {nil, nil, "given "}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) UnsafePointer() { + someStr := "" + + sig := reflect.TypeOf(func() unsafe.Pointer { return nil }) + cases := []returnTestCase{ + // Identical types. + {unsafe.Pointer(&someStr), unsafe.Pointer(&someStr), ""}, + + // Nil values. + {(interface{})(nil), unsafe.Pointer(nil), ""}, + {unsafe.Pointer(nil), unsafe.Pointer(nil), ""}, + + // Wrong types. + {(func())(nil), nil, "given func()"}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {(*string)(&someStr), nil, "given *string"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) UserDefinedNumericType() { + type myType int16 + + sig := reflect.TypeOf(func() myType { return 0 }) + cases := []returnTestCase{ + // Identical types. + {myType(math.MinInt16), myType(math.MinInt16), ""}, + {myType(math.MaxInt16), myType(math.MaxInt16), ""}, + + // In-range ints. + {int(math.MinInt16), myType(math.MinInt16), ""}, + {int(math.MaxInt16), myType(math.MaxInt16), ""}, + + // Out of range ints. + {int(math.MinInt16 - 1), nil, "out of range"}, + {int(math.MaxInt16 + 1), nil, "out of range"}, + + // Wrong types. + {nil, nil, "given "}, + {int16(1), nil, "given int16"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} + +func (t *ReturnTest) UserDefinedNonNumericType() { + type myType string + + sig := reflect.TypeOf(func() myType { return "" }) + cases := []returnTestCase{ + // Identical types. + {myType("taco"), myType("taco"), ""}, + + // Wrong types. + {nil, nil, "given "}, + {int(1), nil, "given int"}, + {float64(1), nil, "given float64"}, + {complex128(1), nil, "given complex128"}, + {string(""), nil, "given string"}, + {&someInt, nil, "given *int"}, + {make(chan int), nil, "given chan int"}, + } + + t.runTestCases(sig, cases) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/sample/mock_io/mock_io.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/sample/mock_io/mock_io.go new file mode 100644 index 0000000..0adb0cd --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/sample/mock_io/mock_io.go @@ -0,0 +1,71 @@ +// This file was auto-generated using createmock. See the following page for +// more information: +// +// https://github.com/smartystreets/assertions/internal/oglemock +// + +package mock_io + +import ( + fmt "fmt" + oglemock "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + io "io" + runtime "runtime" + unsafe "unsafe" +) + +type MockReader interface { + io.Reader + oglemock.MockObject +} + +type mockReader struct { + controller oglemock.Controller + description string +} + +func NewMockReader( + c oglemock.Controller, + desc string) MockReader { + return &mockReader{ + controller: c, + description: desc, + } +} + +func (m *mockReader) Oglemock_Id() uintptr { + return uintptr(unsafe.Pointer(m)) +} + +func (m *mockReader) Oglemock_Description() string { + return m.description +} + +func (m *mockReader) Read(p0 []uint8) (o0 int, o1 error) { + // Get a file name and line number for the caller. + _, file, line, _ := runtime.Caller(1) + + // Hand the call off to the controller, which does most of the work. + retVals := m.controller.HandleMethodCall( + m, + "Read", + file, + line, + []interface{}{p0}) + + if len(retVals) != 2 { + panic(fmt.Sprintf("mockReader.Read: invalid return values: %v", retVals)) + } + + // o0 int + if retVals[0] != nil { + o0 = retVals[0].(int) + } + + // o1 error + if retVals[1] != nil { + o1 = retVals[1].(error) + } + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/save_arg.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/save_arg.go new file mode 100644 index 0000000..27cfcf6 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/save_arg.go @@ -0,0 +1,83 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock + +import ( + "fmt" + "reflect" +) + +// Create an Action that saves the argument at the given zero-based index to +// the supplied destination, which must be a pointer to a type that is +// assignable from the argument type. +func SaveArg(index int, dst interface{}) Action { + return &saveArg{ + index: index, + dstPointer: dst, + } +} + +type saveArg struct { + index int + dstPointer interface{} + + // Set by SetSignature. + dstValue reflect.Value +} + +func (a *saveArg) SetSignature(signature reflect.Type) (err error) { + // Extract the source type. + if a.index >= signature.NumIn() { + err = fmt.Errorf( + "Out of range argument index %v for function type %v", + a.index, + signature) + return + } + + srcType := signature.In(a.index) + + // The destination must be a pointer. + v := reflect.ValueOf(a.dstPointer) + if v.Kind() != reflect.Ptr { + err = fmt.Errorf("Destination is %v, not a pointer", v.Kind()) + return + } + + // Dereference the pointer. + if v.IsNil() { + err = fmt.Errorf("Destination pointer must be non-nil") + return + } + + a.dstValue = v.Elem() + + // The destination must be assignable from the source. + if !srcType.AssignableTo(a.dstValue.Type()) { + err = fmt.Errorf( + "%v is not assignable to %v", + srcType, + a.dstValue.Type()) + return + } + + return +} + +func (a *saveArg) Invoke(methodArgs []interface{}) (rets []interface{}) { + a.dstValue.Set(reflect.ValueOf(methodArgs[a.index])) + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/oglemock/save_arg_test.go b/vendor/github.com/smartystreets/assertions/internal/oglemock/save_arg_test.go new file mode 100644 index 0000000..41e5608 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/oglemock/save_arg_test.go @@ -0,0 +1,132 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oglemock_test + +import ( + "io" + "os" + "reflect" + "testing" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" +) + +func TestSaveArg(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////// +// Boilerplate +//////////////////////////////////////////////////////////// + +type SaveArgTest struct { +} + +func init() { RegisterTestSuite(&SaveArgTest{}) } + +//////////////////////////////////////////////////////////// +// Test functions +//////////////////////////////////////////////////////////// + +func (t *SaveArgTest) FunctionHasNoArguments() { + const index = 0 + var dst int + f := func() (int, string) { return 0, "" } + + err := oglemock.SaveArg(index, &dst).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("index 0"))) + ExpectThat(err, Error(HasSubstr("Out of range"))) + ExpectThat(err, Error(HasSubstr("func() (int, string)"))) +} + +func (t *SaveArgTest) ArgumentIndexOutOfRange() { + const index = 2 + var dst int + f := func(a int, b int) {} + + err := oglemock.SaveArg(index, &dst).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("index 2"))) + ExpectThat(err, Error(HasSubstr("Out of range"))) + ExpectThat(err, Error(HasSubstr("func(int, int)"))) +} + +func (t *SaveArgTest) DestinationIsLiteralNil() { + const index = 0 + f := func(a int, b int) {} + + err := oglemock.SaveArg(index, nil).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("not a pointer"))) +} + +func (t *SaveArgTest) DestinationIsNotAPointer() { + const index = 0 + f := func(a int, b int) {} + + err := oglemock.SaveArg(index, uint(17)).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("pointer"))) + ExpectThat(err, Error(HasSubstr("uint"))) +} + +func (t *SaveArgTest) DestinationIsNilPointer() { + const index = 1 + var dst *int + f := func(a int, b int) {} + + err := oglemock.SaveArg(index, dst).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("pointer"))) + ExpectThat(err, Error(HasSubstr("non-nil"))) +} + +func (t *SaveArgTest) DestinationNotAssignableFromSource() { + const index = 1 + var dst int + f := func(a int, b string) {} + + err := oglemock.SaveArg(index, &dst).SetSignature(reflect.TypeOf(f)) + ExpectThat(err, Error(HasSubstr("int"))) + ExpectThat(err, Error(HasSubstr("assignable"))) + ExpectThat(err, Error(HasSubstr("string"))) +} + +func (t *SaveArgTest) ExactTypeMatch() { + const index = 1 + var dst int + f := func(a int, b int) {} + + action := oglemock.SaveArg(index, &dst) + AssertEq(nil, action.SetSignature(reflect.TypeOf(f))) + + var a int = 17 + var b int = 19 + _ = action.Invoke([]interface{}{a, b}) + + ExpectEq(19, dst) +} + +func (t *SaveArgTest) AssignableTypeMatch() { + const index = 1 + var dst io.Reader + f := func(a int, b *os.File) {} + + action := oglemock.SaveArg(index, &dst) + AssertEq(nil, action.SetSignature(reflect.TypeOf(f))) + + var a int = 17 + var b *os.File = os.Stdout + _ = action.Invoke([]interface{}{a, b}) + + ExpectEq(os.Stdout, dst) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/LICENSE b/vendor/github.com/smartystreets/assertions/internal/ogletest/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/README.md b/vendor/github.com/smartystreets/assertions/internal/ogletest/README.md new file mode 100644 index 0000000..8e54862 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/README.md @@ -0,0 +1,151 @@ +[![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/ogletest?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/ogletest) + +`ogletest` is a unit testing framework for Go with the following features: + + * An extensive and extensible set of matchers for expressing expectations. + * Automatic failure messages; no need to say `t.Errorf("Expected %v, got + %v"...)`. + * Clean, readable output that tells you exactly what you need to know. + * Built-in support for mocking through the [oglemock][] package. + * Style and semantics similar to [Google Test][googletest] and + [Google JS Test][google-js-test]. + +It integrates with Go's built-in `testing` package, so it works with the +`go test` command, and even with other types of test within your package. Unlike +the `testing` package which offers only basic capabilities for signalling +failures, it offers ways to express expectations and get nice failure messages +automatically. + + +Installation +------------ + +First, make sure you have installed Go 1.0.2 or newer. See +[here][golang-install] for instructions. + +Use the following command to install `ogletest` and its dependencies, and to +keep them up to date: + + go get -u github.com/smartystreets/assertions/internal/ogletest + + +Documentation +------------- + +See [here][reference] for package documentation containing an exhaustive list of +exported symbols. Alternatively, you can install the package and then use +`godoc`: + + godoc github.com/smartystreets/assertions/internal/ogletest + +An important part of `ogletest` is its use of matchers provided by the +[oglematchers][matcher-reference] package. See that package's documentation +for information on the built-in matchers available, and check out the +`oglematchers.Matcher` interface if you want to define your own. + + +Example +------- + +Let's say you have a function in your package `people` with the following +signature: + +```go +// GetRandomPerson returns the name and phone number of Tony, Dennis, or Scott. +func GetRandomPerson() (name, phone string) { + [...] +} +``` + +A silly function, but it will do for an example. You can write a couple of tests +for it as follows: + +```go +package people + +import ( + "github.com/smartystreets/assertions/internal/oglematchers" + "github.com/smartystreets/assertions/internal/ogletest" + "testing" +) + +// Give ogletest a chance to run your tests when invoked by 'go test'. +func TestOgletest(t *testing.T) { ogletest.RunTests(t) } + +// Create a test suite, which groups together logically related test methods +// (defined below). You can share common setup and teardown code here; see the +// package docs for more info. +type PeopleTest struct {} +func init() { ogletest.RegisterTestSuite(&PeopleTest{}) } + +func (t *PeopleTest) ReturnsCorrectNames() { + // Call the function a few times, and make sure it never strays from the set + // of expected names. + for i := 0; i < 25; i++ { + name, _ := GetRandomPerson() + ogletest.ExpectThat(name, oglematchers.AnyOf("Tony", "Dennis", "Scott")) + } +} + +func (t *PeopleTest) FormatsPhoneNumbersCorrectly() { + // Call the function a few times, and make sure it returns phone numbers in a + // standard US format. + for i := 0; i < 25; i++ { + _, phone := GetRandomPerson() + ogletest.ExpectThat(phone, oglematchers.MatchesRegexp(`^\(\d{3}\) \d{3}-\d{4}$`)) +} +``` + +Note that test control functions (`RunTests`, `ExpectThat`, and so on) are part +of the `ogletest` package, whereas built-in matchers (`AnyOf`, `MatchesRegexp`, +and more) are part of the [oglematchers][matcher-reference] library. You can of +course use dot imports so that you don't need to prefix each function with its +package name: + +```go +import ( + . "github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/smartystreets/assertions/internal/ogletest" +) +``` + +If you save the test in a file whose name ends in `_test.go`, you can run your +tests by simply invoking the following in your package directory: + + go test + +Here's what the failure output of ogletest looks like, if your function's +implementation is bad. + + [----------] Running tests from PeopleTest + [ RUN ] PeopleTest.FormatsPhoneNumbersCorrectly + people_test.go:32: + Expected: matches regexp "^\(\d{3}\) \d{3}-\d{4}$" + Actual: +1 800 555 5555 + + [ FAILED ] PeopleTest.FormatsPhoneNumbersCorrectly + [ RUN ] PeopleTest.ReturnsCorrectNames + people_test.go:23: + Expected: or(Tony, Dennis, Scott) + Actual: Bart + + [ FAILED ] PeopleTest.ReturnsCorrectNames + [----------] Finished with tests from PeopleTest + +And if the test passes: + + [----------] Running tests from PeopleTest + [ RUN ] PeopleTest.FormatsPhoneNumbersCorrectly + [ OK ] PeopleTest.FormatsPhoneNumbersCorrectly + [ RUN ] PeopleTest.ReturnsCorrectNames + [ OK ] PeopleTest.ReturnsCorrectNames + [----------] Finished with tests from PeopleTest + + +[reference]: http://godoc.org/github.com/smartystreets/assertions/internal/ogletest +[matcher-reference]: http://godoc.org/github.com/smartystreets/assertions/internal/oglematchers +[golang-install]: http://golang.org/doc/install.html +[googletest]: http://code.google.com/p/googletest/ +[google-js-test]: http://code.google.com/p/google-js-test/ +[howtowrite]: http://golang.org/doc/code.html +[oglemock]: https://github.com/smartystreets/assertions/internal/oglemock diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/assert_aliases.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/assert_aliases.go new file mode 100644 index 0000000..fe917b9 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/assert_aliases.go @@ -0,0 +1,70 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +// AssertEq(e, a) is equivalent to AssertThat(a, oglematchers.Equals(e)). +func AssertEq(expected, actual interface{}, errorParts ...interface{}) { + assertThat( + actual, + oglematchers.Equals(expected), + 1, + errorParts) +} + +// AssertNe(e, a) is equivalent to +// AssertThat(a, oglematchers.Not(oglematchers.Equals(e))). +func AssertNe(expected, actual interface{}, errorParts ...interface{}) { + assertThat( + actual, + oglematchers.Not(oglematchers.Equals(expected)), + 1, + errorParts) +} + +// AssertLt(x, y) is equivalent to AssertThat(x, oglematchers.LessThan(y)). +func AssertLt(x, y interface{}, errorParts ...interface{}) { + assertThat(x, oglematchers.LessThan(y), 1, errorParts) +} + +// AssertLe(x, y) is equivalent to AssertThat(x, oglematchers.LessOrEqual(y)). +func AssertLe(x, y interface{}, errorParts ...interface{}) { + assertThat(x, oglematchers.LessOrEqual(y), 1, errorParts) +} + +// AssertGt(x, y) is equivalent to AssertThat(x, oglematchers.GreaterThan(y)). +func AssertGt(x, y interface{}, errorParts ...interface{}) { + assertThat(x, oglematchers.GreaterThan(y), 1, errorParts) +} + +// AssertGe(x, y) is equivalent to +// AssertThat(x, oglematchers.GreaterOrEqual(y)). +func AssertGe(x, y interface{}, errorParts ...interface{}) { + assertThat(x, oglematchers.GreaterOrEqual(y), 1, errorParts) +} + +// AssertTrue(b) is equivalent to AssertThat(b, oglematchers.Equals(true)). +func AssertTrue(b interface{}, errorParts ...interface{}) { + assertThat(b, oglematchers.Equals(true), 1, errorParts) +} + +// AssertFalse(b) is equivalent to AssertThat(b, oglematchers.Equals(false)). +func AssertFalse(b interface{}, errorParts ...interface{}) { + assertThat(b, oglematchers.Equals(false), 1, errorParts) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/assert_that.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/assert_that.go new file mode 100644 index 0000000..2b6fe94 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/assert_that.go @@ -0,0 +1,46 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +func assertThat( + x interface{}, + m oglematchers.Matcher, + depth int, + errorParts []interface{}) { + passed := expectThat(x, m, depth+1, errorParts) + if !passed { + AbortTest() + } +} + +// AssertThat is identical to ExpectThat, except that in the event of failure +// it halts the currently running test immediately. It is thus useful for +// things like bounds checking: +// +// someSlice := [...] +// AssertEq(1, len(someSlice)) // Protects next line from panicking. +// ExpectEq("taco", someSlice[0]) +// +func AssertThat( + x interface{}, + m oglematchers.Matcher, + errorParts ...interface{}) { + assertThat(x, m, 1, errorParts) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/doc.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/doc.go new file mode 100644 index 0000000..bf6507f --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/doc.go @@ -0,0 +1,51 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ogletest provides a framework for writing expressive unit tests. It +// integrates with the builtin testing package, so it works with the gotest +// command. Unlike the testing package which offers only basic capabilities for +// signalling failures, it offers ways to express expectations and get nice +// failure messages automatically. +// +// For example: +// +// //////////////////////////////////////////////////////////////////////// +// // testing package test +// //////////////////////////////////////////////////////////////////////// +// +// someStr, err := ComputeSomeString() +// if err != nil { +// t.Errorf("ComputeSomeString: expected nil error, got %v", err) +// } +// +// !strings.Contains(someStr, "foo") { +// t.Errorf("ComputeSomeString: expected substring foo, got %v", someStr) +// } +// +// //////////////////////////////////////////////////////////////////////// +// // ogletest test +// //////////////////////////////////////////////////////////////////////// +// +// someStr, err := ComputeSomeString() +// ExpectEq(nil, err) +// ExpectThat(someStr, HasSubstr("foo") +// +// Failure messages require no work from the user, and look like the following: +// +// foo_test.go:103: +// Expected: has substring "foo" +// Actual: "bar baz" +// +package ogletest diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_aliases.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_aliases.go new file mode 100644 index 0000000..13e5740 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_aliases.go @@ -0,0 +1,64 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + +// ExpectEq(e, a) is equivalent to ExpectThat(a, oglematchers.Equals(e)). +func ExpectEq(expected, actual interface{}, errorParts ...interface{}) { + expectThat(actual, oglematchers.Equals(expected), 1, errorParts) +} + +// ExpectNe(e, a) is equivalent to +// ExpectThat(a, oglematchers.Not(oglematchers.Equals(e))). +func ExpectNe(expected, actual interface{}, errorParts ...interface{}) { + expectThat( + actual, + oglematchers.Not(oglematchers.Equals(expected)), + 1, + errorParts) +} + +// ExpectLt(x, y) is equivalent to ExpectThat(x, oglematchers.LessThan(y)). +func ExpectLt(x, y interface{}, errorParts ...interface{}) { + expectThat(x, oglematchers.LessThan(y), 1, errorParts) +} + +// ExpectLe(x, y) is equivalent to ExpectThat(x, oglematchers.LessOrEqual(y)). +func ExpectLe(x, y interface{}, errorParts ...interface{}) { + expectThat(x, oglematchers.LessOrEqual(y), 1, errorParts) +} + +// ExpectGt(x, y) is equivalent to ExpectThat(x, oglematchers.GreaterThan(y)). +func ExpectGt(x, y interface{}, errorParts ...interface{}) { + expectThat(x, oglematchers.GreaterThan(y), 1, errorParts) +} + +// ExpectGe(x, y) is equivalent to +// ExpectThat(x, oglematchers.GreaterOrEqual(y)). +func ExpectGe(x, y interface{}, errorParts ...interface{}) { + expectThat(x, oglematchers.GreaterOrEqual(y), 1, errorParts) +} + +// ExpectTrue(b) is equivalent to ExpectThat(b, oglematchers.Equals(true)). +func ExpectTrue(b interface{}, errorParts ...interface{}) { + expectThat(b, oglematchers.Equals(true), 1, errorParts) +} + +// ExpectFalse(b) is equivalent to ExpectThat(b, oglematchers.Equals(false)). +func ExpectFalse(b interface{}, errorParts ...interface{}) { + expectThat(b, oglematchers.Equals(false), 1, errorParts) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_call.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_call.go new file mode 100644 index 0000000..3116f3e --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_call.go @@ -0,0 +1,59 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" + "runtime" +) + +// ExpectCall expresses an expectation that the method of the given name +// should be called on the supplied mock object. It returns a function that +// should be called with the expected arguments, matchers for the arguments, +// or a mix of both. +// +// For example: +// +// mockWriter := [...] +// ogletest.ExpectCall(mockWriter, "Write")(oglematchers.ElementsAre(0x1)) +// .WillOnce(oglemock.Return(1, nil)) +// +// This is a shortcut for calling i.MockController.ExpectCall, where i is the +// TestInfo struct for the currently-running test. Unlike that direct approach, +// this function automatically sets the correct file name and line number for +// the expectation. +func ExpectCall(o oglemock.MockObject, method string) oglemock.PartialExpecation { + // Get information about the call site. + _, file, lineNumber, ok := runtime.Caller(1) + if !ok { + panic("ExpectCall: runtime.Caller") + } + + // Grab the current test info. + info := currentlyRunningTest + if info == nil { + panic("ExpectCall: no test info.") + } + + // Grab the mock controller. + controller := currentlyRunningTest.MockController + if controller == nil { + panic("ExpectCall: no mock controller.") + } + + // Report the expectation. + return controller.ExpectCall(o, method, file, lineNumber) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_that.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_that.go new file mode 100644 index 0000000..55b2d8c --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_that.go @@ -0,0 +1,100 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "fmt" + "path" + "reflect" + "runtime" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +// ExpectThat confirms that the supplied matcher matches the value x, adding a +// failure record to the currently running test if it does not. If additional +// parameters are supplied, the first will be used as a format string for the +// later ones, and the user-supplied error message will be added to the test +// output in the event of a failure. +// +// For example: +// +// ExpectThat(userName, Equals("jacobsa")) +// ExpectThat(users[i], Equals("jacobsa"), "while processing user %d", i) +// +func ExpectThat( + x interface{}, + m oglematchers.Matcher, + errorParts ...interface{}) { + expectThat(x, m, 1, errorParts) +} + +// The generalized form of ExpectThat. depth is the distance on the stack +// between the caller's frame and the user's frame. Returns passed iff the +// match succeeded. +func expectThat( + x interface{}, + m oglematchers.Matcher, + depth int, + errorParts []interface{}) (passed bool) { + // Check whether the value matches. If it does, we are finished. + matcherErr := m.Matches(x) + if matcherErr == nil { + passed = true + return + } + + var r FailureRecord + + // Get information about the call site. + var ok bool + if _, r.FileName, r.LineNumber, ok = runtime.Caller(depth + 1); !ok { + panic("expectThat: runtime.Caller") + } + + r.FileName = path.Base(r.FileName) + + // Create an appropriate failure message. Make sure that the expected and + // actual values align properly. + relativeClause := "" + if matcherErr.Error() != "" { + relativeClause = fmt.Sprintf(", %s", matcherErr.Error()) + } + + r.Error = fmt.Sprintf( + "Expected: %s\nActual: %v%s", + m.Description(), + x, + relativeClause) + + // Add the user error, if any. + if len(errorParts) != 0 { + v := reflect.ValueOf(errorParts[0]) + if v.Kind() != reflect.String { + panic(fmt.Sprintf("ExpectThat: invalid format string type %v", v.Kind())) + } + + r.Error = fmt.Sprintf( + "%s\n%s", + r.Error, + fmt.Sprintf(v.String(), errorParts[1:]...)) + } + + // Report the failure. + AddFailureRecord(r) + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_that_test.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_that_test.go new file mode 100644 index 0000000..11a5498 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/expect_that_test.go @@ -0,0 +1,168 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "errors" + "testing" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +// Set up a new test state with empty fields. +func setUpCurrentTest() { + currentlyRunningTest = newTestInfo() +} + +type fakeExpectThatMatcher struct { + desc string + err error +} + +func (m *fakeExpectThatMatcher) Matches(c interface{}) error { + return m.err +} + +func (m *fakeExpectThatMatcher) Description() string { + return m.desc +} + +func assertEqInt(t *testing.T, e, c int) { + if e != c { + t.Fatalf("Expected %d, got %d", e, c) + } +} + +func expectEqInt(t *testing.T, e, c int) { + if e != c { + t.Errorf("Expected %v, got %v", e, c) + } +} + +func expectEqStr(t *testing.T, e, c string) { + if e != c { + t.Errorf("Expected %s, got %s", e, c) + } +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func TestNoCurrentTest(t *testing.T) { + panicked := false + + defer func() { + if !panicked { + t.Errorf("Expected panic; got none.") + } + }() + + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + + currentlyRunningTest = nil + ExpectThat(17, Equals(19)) +} + +func TestNoFailure(t *testing.T) { + setUpCurrentTest() + matcher := &fakeExpectThatMatcher{"", nil} + ExpectThat(17, matcher) + + assertEqInt(t, 0, len(currentlyRunningTest.failureRecords)) +} + +func TestInvalidFormatString(t *testing.T) { + panicked := false + + defer func() { + if !panicked { + t.Errorf("Expected panic; got none.") + } + }() + + defer func() { + if r := recover(); r != nil { + panicked = true + } + }() + + setUpCurrentTest() + matcher := &fakeExpectThatMatcher{"", errors.New("")} + ExpectThat(17, matcher, 19, "blah") +} + +func TestNoMatchWithoutErrorText(t *testing.T) { + setUpCurrentTest() + matcher := &fakeExpectThatMatcher{"taco", errors.New("")} + ExpectThat(17, matcher) + + assertEqInt(t, 1, len(currentlyRunningTest.failureRecords)) + + record := currentlyRunningTest.failureRecords[0] + expectEqStr(t, "expect_that_test.go", record.FileName) + expectEqInt(t, 119, record.LineNumber) + expectEqStr(t, "Expected: taco\nActual: 17", record.Error) +} + +func TestNoMatchWithErrorTExt(t *testing.T) { + setUpCurrentTest() + matcher := &fakeExpectThatMatcher{"taco", errors.New("which is foo")} + ExpectThat(17, matcher) + + assertEqInt(t, 1, len(currentlyRunningTest.failureRecords)) + record := currentlyRunningTest.failureRecords[0] + + expectEqStr( + t, + "Expected: taco\nActual: 17, which is foo", + record.Error) +} + +func TestFailureWithUserMessage(t *testing.T) { + setUpCurrentTest() + matcher := &fakeExpectThatMatcher{"taco", errors.New("")} + ExpectThat(17, matcher, "Asd: %d %s", 19, "taco") + + assertEqInt(t, 1, len(currentlyRunningTest.failureRecords)) + record := currentlyRunningTest.failureRecords[0] + + expectEqStr(t, "Expected: taco\nActual: 17\nAsd: 19 taco", record.Error) +} + +func TestAdditionalFailure(t *testing.T) { + setUpCurrentTest() + matcher := &fakeExpectThatMatcher{"", errors.New("")} + + // Fail twice. + ExpectThat(17, matcher, "taco") + ExpectThat(19, matcher, "burrito") + + assertEqInt(t, 2, len(currentlyRunningTest.failureRecords)) + record1 := currentlyRunningTest.failureRecords[0] + record2 := currentlyRunningTest.failureRecords[1] + + expectEqStr(t, "Expected: \nActual: 17\ntaco", record1.Error) + expectEqStr(t, "Expected: \nActual: 19\nburrito", record2.Error) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/failure.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/failure.go new file mode 100644 index 0000000..95be2cf --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/failure.go @@ -0,0 +1,90 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "fmt" + "path" + "runtime" +) + +// FailureRecord represents a single failed expectation or assertion for a +// test. Most users don't want to interact with these directly; they are +// generated implicitly using ExpectThat, AssertThat, ExpectLt, etc. +type FailureRecord struct { + // The file name within which the expectation failed, e.g. "foo_test.go". + FileName string + + // The line number at which the expectation failed. + LineNumber int + + // The error associated with the file:line pair above. For example, the + // following expectation: + // + // ExpectEq(17, "taco")" + // + // May cause this error: + // + // Expected: 17 + // Actual: "taco", which is not numeric + // + Error string +} + +// Record a failure for the currently running test (and continue running it). +// Most users will want to use ExpectThat, ExpectEq, etc. instead of this +// function. Those that do want to report arbitrary errors will probably be +// satisfied with AddFailure, which is easier to use. +func AddFailureRecord(r FailureRecord) { + currentlyRunningTest.mu.Lock() + defer currentlyRunningTest.mu.Unlock() + + currentlyRunningTest.failureRecords = append( + currentlyRunningTest.failureRecords, + r) +} + +// Call AddFailureRecord with a record whose file name and line number come +// from the caller of this function, and whose error string is created by +// calling fmt.Sprintf using the arguments to this function. +func AddFailure(format string, a ...interface{}) { + r := FailureRecord{ + Error: fmt.Sprintf(format, a...), + } + + // Get information about the call site. + var ok bool + if _, r.FileName, r.LineNumber, ok = runtime.Caller(1); !ok { + panic("Can't find caller") + } + + r.FileName = path.Base(r.FileName) + + AddFailureRecord(r) +} + +// A sentinel type that is used in a conspiracy between AbortTest and runTests. +// If runTests sees an abortError as the value given to a panic() call, it will +// avoid printing the panic error. +type abortError struct { +} + +// Immediately stop executing the running test, causing it to fail with the +// failures previously recorded. Behavior is undefined if no failures have been +// recorded. +func AbortTest() { + panic(abortError{}) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/integration_test.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/integration_test.go new file mode 100644 index 0000000..ec45184 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/integration_test.go @@ -0,0 +1,265 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest_test + +import ( + "errors" + "flag" + "fmt" + "go/build" + "io/ioutil" + "os" + "os/exec" + "path" + "regexp" + "strings" + "syscall" + "testing" +) + +const ogletestPkg = "github.com/smartystreets/assertions/internal/ogletest" + +var dumpNew = flag.Bool("dump_new", false, "Dump new golden files.") +var objDir string + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +// Install the possibly locally-modified copy of ogletest, so that these +// integration tests run using the package currently being worked on by the +// programmer. Also install other dependencies needed by the test cases, so +// that `go test` complaining about non-up-to-date packages doesn't make it +// into the golden files. +func installLocalPackages() error { + cmd := exec.Command( + "go", + "install", + ogletestPkg, + "github.com/smartystreets/assertions/internal/oglemock", + "github.com/smartystreets/assertions/internal/ogletest/test_cases/mock_image") + + output, err := cmd.CombinedOutput() + + if err != nil { + return errors.New(fmt.Sprintf("%v:\n%s", err, output)) + } + + return nil +} + +// getCaseNames looks for integration test cases as files in the test_cases +// directory. +func getCaseNames() ([]string, error) { + // Open the test cases directory. + dir, err := os.Open("test_cases") + if err != nil { + return nil, errors.New(fmt.Sprintf("Opening dir: %v", err)) + } + + // Get a list of the names in the directory. + names, err := dir.Readdirnames(0) + if err != nil { + return nil, errors.New(fmt.Sprintf("Readdirnames: %v", err)) + } + + // Filter the names. + result := make([]string, len(names)) + resultLen := 0 + for _, name := range names { + // Skip golden files and hidden files. + if strings.HasPrefix(name, "golden.") || strings.HasPrefix(name, ".") { + continue + } + + // Check for the right format. + if !strings.HasSuffix(name, ".test.go") { + continue + } + + // Store the name minus the extension. + result[resultLen] = name[:len(name)-8] + resultLen++ + } + + return result[:resultLen], nil +} + +func writeContentsToFileOrDie(contents []byte, path string) { + if err := ioutil.WriteFile(path, contents, 0600); err != nil { + panic("ioutil.WriteFile: " + err.Error()) + } +} + +func readFileOrDie(path string) []byte { + contents, err := ioutil.ReadFile(path) + if err != nil { + panic("ioutil.ReadFile: " + err.Error()) + } + + return contents +} + +// cleanOutput transforms the supplied output so that it no longer contains +// information that changes from run to run, making the golden tests less +// flaky. +func cleanOutput(o []byte, testPkg string) []byte { + // Replace references to the last component of the test package name, which + // contains a unique number. + o = []byte(strings.Replace(string(o), path.Base(testPkg), "somepkg", -1)) + + // Replace things that look like line numbers and process counters in stack + // traces. + stackFrameRe := regexp.MustCompile(`\t\S+\.(c|go):\d+`) + o = stackFrameRe.ReplaceAll(o, []byte("\tsome_file.txt:0")) + + // Replace full paths in failure messages with fake paths. + pathRe := regexp.MustCompile(`/\S+/(\w+\.(?:go|s):\d+)`) + o = pathRe.ReplaceAll(o, []byte("/some/path/$1")) + + // Replace unstable timings in gotest fail messages. + timingRe1 := regexp.MustCompile(`--- FAIL: .* \(\d\.\d{2}s\)`) + o = timingRe1.ReplaceAll(o, []byte("--- FAIL: TestSomething (1.23s)")) + + timingRe2 := regexp.MustCompile(`FAIL.*somepkg\s*\d\.\d{2,}s`) + o = timingRe2.ReplaceAll(o, []byte("FAIL somepkg 1.234s")) + + timingRe3 := regexp.MustCompile(`ok.*somepkg\s*\d\.\d{2,}s`) + o = timingRe3.ReplaceAll(o, []byte("ok somepkg 1.234s")) + + timingRe4 := regexp.MustCompile(`SlowTest \([0-9.]+ms\)`) + o = timingRe4.ReplaceAll(o, []byte("SlowTest (1234ms)")) + + return o +} + +// Create a temporary package directory somewhere that 'go test' can find, and +// return the directory and package name. +func createTempPackageDir(caseName string) (dir, pkg string) { + // Figure out where the local source code for ogletest is. + buildPkg, err := build.Import(ogletestPkg, "", build.FindOnly) + if err != nil { + panic("Finding ogletest tree: " + err.Error()) + } + + // Create a temporary directory underneath this. + ogletestPkgDir := buildPkg.Dir + prefix := fmt.Sprintf("tmp-%s-", caseName) + + dir, err = ioutil.TempDir(ogletestPkgDir, prefix) + if err != nil { + panic("ioutil.TempDir: " + err.Error()) + } + + pkg = path.Join("github.com/smartystreets/assertions/internal/ogletest", dir[len(ogletestPkgDir):]) + return +} + +// runTestCase runs the case with the supplied name (e.g. "passing"), and +// returns its output and exit code. +func runTestCase(name string) ([]byte, int, error) { + // Create a temporary directory for the test files. + testDir, testPkg := createTempPackageDir(name) + defer os.RemoveAll(testDir) + + // Create the test source file. + sourceFile := name + ".test.go" + testContents := readFileOrDie(path.Join("test_cases", sourceFile)) + writeContentsToFileOrDie(testContents, path.Join(testDir, name+"_test.go")) + + // Invoke 'go test'. Use the package directory as working dir instead of + // giving the package name as an argument so that 'go test' prints passing + // test output. Special case: pass a test filter to the filtered case. + cmd := exec.Command("go", "test") + if name == "filtered" { + cmd.Args = append(cmd.Args, "--ogletest.run=Test(Bar|Baz)") + } + + cmd.Dir = testDir + output, err := cmd.CombinedOutput() + + // Clean up the process's output. + output = cleanOutput(output, testPkg) + + // Did the process exist with zero code? + if err == nil { + return output, 0, nil + } + + // Make sure the process actually exited. + exitError, ok := err.(*exec.ExitError) + if !ok || !exitError.Exited() { + return nil, 0, errors.New("exec.Command.Output: " + err.Error()) + } + + return output, exitError.Sys().(syscall.WaitStatus).ExitStatus(), nil +} + +// checkGolden file checks the supplied actual output for the named test case +// against the golden file for that case. If requested by the user, it rewrites +// the golden file on failure. +func checkAgainstGoldenFile(caseName string, output []byte) bool { + goldenFile := path.Join("test_cases", "golden."+caseName+"_test") + goldenContents := readFileOrDie(goldenFile) + + result := string(output) == string(goldenContents) + if !result && *dumpNew { + writeContentsToFileOrDie(output, goldenFile) + } + + return result +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func TestGoldenFiles(t *testing.T) { + // Ensure the local package is installed. This will prevent the test cases + // from using the installed version, which may be out of date. + err := installLocalPackages() + if err != nil { + t.Fatalf("Error installing local ogletest: %v", err) + } + + // We expect there to be at least one case. + caseNames, err := getCaseNames() + if err != nil || len(caseNames) == 0 { + t.Fatalf("Error getting cases: %v", err) + } + + // Run each test case. + for _, caseName := range caseNames { + // Run the test case. + output, exitCode, err := runTestCase(caseName) + if err != nil { + t.Fatalf("Running test case %s: %v", caseName, err) + } + + // Check the status code. We assume all test cases fail except for the + // passing one. + shouldPass := (caseName == "passing" || caseName == "no_cases") + didPass := exitCode == 0 + if shouldPass != didPass { + t.Errorf("Bad exit code for test case %s: %d", caseName, exitCode) + } + + // Check the output against the golden file. + if !checkAgainstGoldenFile(caseName, output) { + t.Errorf("Output for test case %s doesn't match golden file.", caseName) + } + } +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/register.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/register.go new file mode 100644 index 0000000..756f2aa --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/register.go @@ -0,0 +1,86 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +// The input to ogletest.Register. Most users will want to use +// ogletest.RegisterTestSuite. +// +// A test suite is the basic unit of registration in ogletest. It consists of +// zero or more named test functions which will be run in sequence, along with +// optional setup and tear-down functions. +type TestSuite struct { + // The name of the overall suite, e.g. "MyPackageTest". + Name string + + // If non-nil, a function that will be run exactly once, before any of the + // test functions are run. + SetUp func() + + // The test functions comprising this suite. + TestFunctions []TestFunction + + // If non-nil, a function that will be run exactly once, after all of the + // test functions have run. + TearDown func() +} + +type TestFunction struct { + // The name of this test function, relative to the suite in which it resides. + // If the name is "TweaksFrobnicator", then the function might be presented + // in the ogletest UI as "FooTest.TweaksFrobnicator". + Name string + + // If non-nil, a function that is run before Run, passed a pointer to a + // struct containing information about the test run. + SetUp func(*TestInfo) + + // The function to invoke for the test body. Must be non-nil. Will not be run + // if SetUp panics. + Run func() + + // If non-nil, a function that is run after Run. + TearDown func() +} + +// Register a test suite for execution by RunTests. +// +// This is the most general registration mechanism. Most users will want +// RegisterTestSuite, which is a wrapper around this function that requires +// less boilerplate. +// +// Panics on invalid input. +func Register(suite TestSuite) { + // Make sure the suite is legal. + if suite.Name == "" { + panic("Test suites must have names.") + } + + for _, tf := range suite.TestFunctions { + if tf.Name == "" { + panic("Test functions must have names.") + } + + if tf.Run == nil { + panic("Test functions must have non-nil run fields.") + } + } + + // Save the suite for later. + registeredSuites = append(registeredSuites, suite) +} + +// The list of test suites previously registered. +var registeredSuites []TestSuite diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/register_test_suite.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/register_test_suite.go new file mode 100644 index 0000000..5ca08d6 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/register_test_suite.go @@ -0,0 +1,193 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "fmt" + "reflect" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil" +) + +// Test suites that implement this interface have special meaning to +// RegisterTestSuite. +type SetUpTestSuiteInterface interface { + // This method will be called exactly once, before the first test method is + // run. The receiver of this method will be a zero value of the test suite + // type, and is not shared with any other methods. Use this method to set up + // any necessary global state shared by all of the test methods. + SetUpTestSuite() +} + +// Test suites that implement this interface have special meaning to +// RegisterTestSuite. +type TearDownTestSuiteInterface interface { + // This method will be called exactly once, after the last test method is + // run. The receiver of this method will be a zero value of the test suite + // type, and is not shared with any other methods. Use this method to clean + // up after any necessary global state shared by all of the test methods. + TearDownTestSuite() +} + +// Test suites that implement this interface have special meaning to +// Register. +type SetUpInterface interface { + // This method is called before each test method is invoked, with the same + // receiver as that test method. At the time this method is invoked, the + // receiver is a zero value for the test suite type. Use this method for + // common setup code that works on data not shared across tests. + SetUp(*TestInfo) +} + +// Test suites that implement this interface have special meaning to +// Register. +type TearDownInterface interface { + // This method is called after each test method is invoked, with the same + // receiver as that test method. Use this method for common cleanup code that + // works on data not shared across tests. + TearDown() +} + +// RegisterTestSuite tells ogletest about a test suite containing tests that it +// should run. Any exported method on the type pointed to by the supplied +// prototype value will be treated as test methods, with the exception of the +// methods defined by the following interfaces, which when present are treated +// as described in the documentation for those interfaces: +// +// * SetUpTestSuiteInterface +// * SetUpInterface +// * TearDownInterface +// * TearDownTestSuiteInterface +// +// Each test method is invoked on a different receiver, which is initially a +// zero value of the test suite type. +// +// Example: +// +// // Some value that is needed by the tests but is expensive to compute. +// var someExpensiveThing uint +// +// type FooTest struct { +// // Path to a temporary file used by the tests. Each test gets a +// // different temporary file. +// tempFile string +// } +// func init() { ogletest.RegisterTestSuite(&FooTest{}) } +// +// func (t *FooTest) SetUpTestSuite() { +// someExpensiveThing = ComputeSomeExpensiveThing() +// } +// +// func (t *FooTest) SetUp(ti *ogletest.TestInfo) { +// t.tempFile = CreateTempFile() +// } +// +// func (t *FooTest) TearDown() { +// DeleteTempFile(t.tempFile) +// } +// +// func (t *FooTest) FrobinicatorIsSuccessfullyTweaked() { +// res := DoSomethingWithExpensiveThing(someExpensiveThing, t.tempFile) +// ExpectThat(res, Equals(true)) +// } +// +func RegisterTestSuite(p interface{}) { + if p == nil { + panic("RegisterTestSuite called with nil suite.") + } + + val := reflect.ValueOf(p) + typ := val.Type() + var zeroInstance reflect.Value + + // We will transform to a TestSuite struct. + suite := TestSuite{} + suite.Name = typ.Elem().Name() + + zeroInstance = reflect.New(typ.Elem()) + if i, ok := zeroInstance.Interface().(SetUpTestSuiteInterface); ok { + suite.SetUp = func() { i.SetUpTestSuite() } + } + + zeroInstance = reflect.New(typ.Elem()) + if i, ok := zeroInstance.Interface().(TearDownTestSuiteInterface); ok { + suite.TearDown = func() { i.TearDownTestSuite() } + } + + // Transform a list of test methods for the suite, filtering them to just the + // ones that we don't need to skip. + for _, method := range filterMethods(suite.Name, srcutil.GetMethodsInSourceOrder(typ)) { + var tf TestFunction + tf.Name = method.Name + + // Create an instance to be operated on by all of the TestFunction's + // internal functions. + instance := reflect.New(typ.Elem()) + + // Bind the functions to the instance. + if i, ok := instance.Interface().(SetUpInterface); ok { + tf.SetUp = func(ti *TestInfo) { i.SetUp(ti) } + } + + methodCopy := method + tf.Run = func() { runTestMethod(instance, methodCopy) } + + if i, ok := instance.Interface().(TearDownInterface); ok { + tf.TearDown = func() { i.TearDown() } + } + + // Save the TestFunction. + suite.TestFunctions = append(suite.TestFunctions, tf) + } + + // Register the suite. + Register(suite) +} + +func runTestMethod(suite reflect.Value, method reflect.Method) { + if method.Func.Type().NumIn() != 1 { + panic(fmt.Sprintf( + "%s: expected 1 args, actually %d.", + method.Name, + method.Func.Type().NumIn())) + } + + method.Func.Call([]reflect.Value{suite}) +} + +func filterMethods(suiteName string, in []reflect.Method) (out []reflect.Method) { + for _, m := range in { + // Skip set up, tear down, and unexported methods. + if isSpecialMethod(m.Name) || !isExportedMethod(m.Name) { + continue + } + + out = append(out, m) + } + + return +} + +func isSpecialMethod(name string) bool { + return (name == "SetUpTestSuite") || + (name == "TearDownTestSuite") || + (name == "SetUp") || + (name == "TearDown") +} + +func isExportedMethod(name string) bool { + return len(name) > 0 && name[0] >= 'A' && name[0] <= 'Z' +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/run_tests.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/run_tests.go new file mode 100644 index 0000000..54e8f46 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/run_tests.go @@ -0,0 +1,354 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "bytes" + "flag" + "fmt" + "os" + "path" + "regexp" + "runtime" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/reqtrace" +) + +var fTestFilter = flag.String( + "ogletest.run", + "", + "Regexp for matching tests to run.") + +var fStopEarly = flag.Bool( + "ogletest.stop_early", + false, + "If true, stop after the first failure.") + +// runTestsOnce protects RunTests from executing multiple times. +var runTestsOnce sync.Once + +func isAbortError(x interface{}) bool { + _, ok := x.(abortError) + return ok +} + +// Run a single test function, returning a slice of failure records. +func runTestFunction(tf TestFunction) (failures []FailureRecord) { + // Set up a clean slate for this test. Make sure to reset it after everything + // below is finished, so we don't accidentally use it elsewhere. + currentlyRunningTest = newTestInfo() + defer func() { + currentlyRunningTest = nil + }() + + ti := currentlyRunningTest + + // Start a trace. + var reportOutcome reqtrace.ReportFunc + ti.Ctx, reportOutcome = reqtrace.Trace(ti.Ctx, tf.Name) + + // Run the SetUp function, if any, paying attention to whether it panics. + setUpPanicked := false + if tf.SetUp != nil { + setUpPanicked = runWithProtection(func() { tf.SetUp(ti) }) + } + + // Run the test function itself, but only if the SetUp function didn't panic. + // (This includes AssertThat errors.) + if !setUpPanicked { + runWithProtection(tf.Run) + } + + // Run the TearDown function, if any. + if tf.TearDown != nil { + runWithProtection(tf.TearDown) + } + + // Tell the mock controller for the tests to report any errors it's sitting + // on. + ti.MockController.Finish() + + // Report the outcome to reqtrace. + if len(ti.failureRecords) == 0 { + reportOutcome(nil) + } else { + reportOutcome(fmt.Errorf("%v failure records", len(ti.failureRecords))) + } + + return ti.failureRecords +} + +// Run everything registered with Register (including via the wrapper +// RegisterTestSuite). +// +// Failures are communicated to the supplied testing.T object. This is the +// bridge between ogletest and the testing package (and `go test`); you should +// ensure that it's called at least once by creating a test function compatible +// with `go test` and calling it there. +// +// For example: +// +// import ( +// "github.com/smartystreets/assertions/internal/ogletest" +// "testing" +// ) +// +// func TestOgletest(t *testing.T) { +// ogletest.RunTests(t) +// } +// +func RunTests(t *testing.T) { + runTestsOnce.Do(func() { runTestsInternal(t) }) +} + +// Signalling between RunTests and StopRunningTests. +var gStopRunning uint64 + +// Request that RunTests stop what it's doing. After the currently running test +// is finished, including tear-down, the program will exit with an error code. +func StopRunningTests() { + atomic.StoreUint64(&gStopRunning, 1) +} + +// runTestsInternal does the real work of RunTests, which simply wraps it in a +// sync.Once. +func runTestsInternal(t *testing.T) { + // Process each registered suite. + for _, suite := range registeredSuites { + // Stop now if we've already seen a failure and we've been told to stop + // early. + if t.Failed() && *fStopEarly { + break + } + + // Print a banner. + fmt.Printf("[----------] Running tests from %s\n", suite.Name) + + // Run the SetUp function, if any. + if suite.SetUp != nil { + suite.SetUp() + } + + // Run each test function that the user has not told us to skip. + stoppedEarly := false + for _, tf := range filterTestFunctions(suite) { + // Did the user request that we stop running tests? If so, skip the rest + // of this suite (and exit after tearing it down). + if atomic.LoadUint64(&gStopRunning) != 0 { + stoppedEarly = true + break + } + + // Print a banner for the start of this test function. + fmt.Printf("[ RUN ] %s.%s\n", suite.Name, tf.Name) + + // Run the test function. + startTime := time.Now() + failures := runTestFunction(tf) + runDuration := time.Since(startTime) + + // Print any failures, and mark the test as having failed if there are any. + for _, record := range failures { + t.Fail() + fmt.Printf( + "%s:%d:\n%s\n\n", + record.FileName, + record.LineNumber, + record.Error) + } + + // Print a banner for the end of the test. + bannerMessage := "[ OK ]" + if len(failures) != 0 { + bannerMessage = "[ FAILED ]" + } + + // Print a summary of the time taken, if long enough. + var timeMessage string + if runDuration >= 25*time.Millisecond { + timeMessage = fmt.Sprintf(" (%s)", runDuration.String()) + } + + fmt.Printf( + "%s %s.%s%s\n", + bannerMessage, + suite.Name, + tf.Name, + timeMessage) + + // Stop running tests from this suite if we've been told to stop early + // and this test failed. + if t.Failed() && *fStopEarly { + break + } + } + + // Run the suite's TearDown function, if any. + if suite.TearDown != nil { + suite.TearDown() + } + + // Were we told to exit early? + if stoppedEarly { + fmt.Println("Exiting early due to user request.") + os.Exit(1) + } + + fmt.Printf("[----------] Finished with tests from %s\n", suite.Name) + } +} + +// Return true iff the supplied program counter appears to lie within panic(). +func isPanic(pc uintptr) bool { + f := runtime.FuncForPC(pc) + if f == nil { + return false + } + + return f.Name() == "runtime.gopanic" || f.Name() == "runtime.sigpanic" +} + +// Find the deepest stack frame containing something that appears to be a +// panic. Return the 'skip' value that a caller to this function would need +// to supply to runtime.Caller for that frame, or a negative number if not found. +func findPanic() int { + localSkip := -1 + for i := 0; ; i++ { + // Stop if we've passed the base of the stack. + pc, _, _, ok := runtime.Caller(i) + if !ok { + break + } + + // Is this a panic? + if isPanic(pc) { + localSkip = i + } + } + + return localSkip - 1 +} + +// Attempt to find the file base name and line number for the ultimate source +// of a panic, on the panicking stack. Return a human-readable sentinel if +// unsuccessful. +func findPanicFileLine() (string, int) { + panicSkip := findPanic() + if panicSkip < 0 { + return "(unknown)", 0 + } + + // Find the trigger of the panic. + _, file, line, ok := runtime.Caller(panicSkip + 1) + if !ok { + return "(unknown)", 0 + } + + return path.Base(file), line +} + +// Run the supplied function, catching panics (including AssertThat errors) and +// reporting them to the currently-running test as appropriate. Return true iff +// the function panicked. +func runWithProtection(f func()) (panicked bool) { + defer func() { + // If the test didn't panic, we're done. + r := recover() + if r == nil { + return + } + + panicked = true + + // We modify the currently running test below. + currentlyRunningTest.mu.Lock() + defer currentlyRunningTest.mu.Unlock() + + // If the function panicked (and the panic was not due to an AssertThat + // failure), add a failure for the panic. + if !isAbortError(r) { + var panicRecord FailureRecord + panicRecord.FileName, panicRecord.LineNumber = findPanicFileLine() + panicRecord.Error = fmt.Sprintf( + "panic: %v\n\n%s", r, formatPanicStack()) + + currentlyRunningTest.failureRecords = append( + currentlyRunningTest.failureRecords, + panicRecord) + } + }() + + f() + return +} + +func formatPanicStack() string { + buf := new(bytes.Buffer) + + // Find the panic. If successful, we'll skip to below it. Otherwise, we'll + // format everything. + var initialSkip int + if panicSkip := findPanic(); panicSkip >= 0 { + initialSkip = panicSkip + 1 + } + + for i := initialSkip; ; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + + // Choose a function name to display. + funcName := "(unknown)" + if f := runtime.FuncForPC(pc); f != nil { + funcName = f.Name() + } + + // Stop if we've gotten as far as the test runner code. + if funcName == "github.com/smartystreets/assertions/internal/ogletest.runTestMethod" || + funcName == "github.com/smartystreets/assertions/internal/ogletest.runWithProtection" { + break + } + + // Add an entry for this frame. + fmt.Fprintf(buf, "%s\n\t%s:%d\n", funcName, file, line) + } + + return buf.String() +} + +// Filter test functions according to the user-supplied filter flag. +func filterTestFunctions(suite TestSuite) (out []TestFunction) { + re, err := regexp.Compile(*fTestFilter) + if err != nil { + panic("Invalid value for --ogletest.run: " + err.Error()) + } + + for _, tf := range suite.TestFunctions { + fullName := fmt.Sprintf("%s.%s", suite.Name, tf.Name) + if !re.MatchString(fullName) { + continue + } + + out = append(out, tf) + } + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/docs.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/docs.go new file mode 100644 index 0000000..d9b9bc8 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/docs.go @@ -0,0 +1,5 @@ +// Copyright 2015 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) + +// Functions for working with source code. +package srcutil diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/methods.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/methods.go new file mode 100644 index 0000000..a8c5828 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/methods.go @@ -0,0 +1,65 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package srcutil + +import ( + "fmt" + "reflect" + "runtime" + "sort" +) + +func getLine(m reflect.Method) int { + pc := m.Func.Pointer() + + f := runtime.FuncForPC(pc) + if f == nil { + panic(fmt.Sprintf("Couldn't get runtime func for method (pc=%d): %v", pc, m)) + } + + _, line := f.FileLine(pc) + return line +} + +type sortableMethodSet []reflect.Method + +func (s sortableMethodSet) Len() int { + return len(s) +} + +func (s sortableMethodSet) Less(i, j int) bool { + return getLine(s[i]) < getLine(s[j]) +} + +func (s sortableMethodSet) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Given a type t, return all of the methods of t sorted such that source file +// order is preserved. Order across files is undefined. Order within lines is +// undefined. +func GetMethodsInSourceOrder(t reflect.Type) []reflect.Method { + // Build the list of methods. + methods := sortableMethodSet{} + for i := 0; i < t.NumMethod(); i++ { + methods = append(methods, t.Method(i)) + } + + // Sort it. + sort.Sort(methods) + + return methods +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/methods_test.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/methods_test.go new file mode 100644 index 0000000..91a8cb3 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil/methods_test.go @@ -0,0 +1,107 @@ +// Copyright 2012 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package srcutil_test + +import ( + "fmt" + "reflect" + "testing" + + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" + . "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/ogletest/srcutil" +) + +func TestRegisterMethodsTest(t *testing.T) { RunTests(t) } + +//////////////////////////////////////////////////////////////////////// +// Helpers +//////////////////////////////////////////////////////////////////////// + +type MethodsTest struct { +} + +func init() { RegisterTestSuite(&MethodsTest{}) } + +type OneMethodType int + +func (x OneMethodType) Foo() {} + +type MultipleMethodsType int + +func (x MultipleMethodsType) Foo() {} +func (x MultipleMethodsType) Bar() {} +func (x MultipleMethodsType) Baz() {} + +type methodNameMatcher struct { + expected string +} + +func (m *methodNameMatcher) Description() string { + return fmt.Sprintf("method named %s", m.expected) +} + +func (m *methodNameMatcher) Matches(x interface{}) error { + method, ok := x.(reflect.Method) + if !ok { + panic("Invalid argument.") + } + + if method.Name != m.expected { + return fmt.Errorf("whose name is %s", method.Name) + } + + return nil +} + +func NameIs(name string) Matcher { + return &methodNameMatcher{name} +} + +//////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////// + +func (t *MethodsTest) NoMethods() { + type foo int + + methods := srcutil.GetMethodsInSourceOrder(reflect.TypeOf(foo(17))) + ExpectThat(methods, ElementsAre()) +} + +func (t *MethodsTest) OneMethod() { + methods := srcutil.GetMethodsInSourceOrder(reflect.TypeOf(OneMethodType(17))) + ExpectThat( + methods, + ElementsAre( + NameIs("Foo"), + )) +} + +func (t *MethodsTest) MultipleMethods() { + methods := srcutil.GetMethodsInSourceOrder(reflect.TypeOf(MultipleMethodsType(17))) + ExpectThat( + methods, + ElementsAre( + NameIs("Foo"), + NameIs("Bar"), + NameIs("Baz"), + )) + + ExpectEq("Foo", methods[0].Name) + ExpectEq("Bar", methods[1].Name) + ExpectEq("Baz", methods[2].Name) +} diff --git a/vendor/github.com/smartystreets/assertions/internal/ogletest/test_info.go b/vendor/github.com/smartystreets/assertions/internal/ogletest/test_info.go new file mode 100644 index 0000000..ef77357 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/ogletest/test_info.go @@ -0,0 +1,91 @@ +// Copyright 2011 Aaron Jacobs. All Rights Reserved. +// Author: aaronjjacobs@gmail.com (Aaron Jacobs) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ogletest + +import ( + "sync" + + "github.com/moul/ssh2docker/vendor/golang.org/x/net/context" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglemock" +) + +// TestInfo represents information about a currently running or previously-run +// test. +type TestInfo struct { + // A mock controller that is set up to report errors to the ogletest test + // runner. This can be used for setting up mock expectations and handling + // mock calls. The Finish method should not be run by the user; ogletest will + // do that automatically after the test's TearDown method is run. + // + // Note that this feature is still experimental, and is subject to change. + MockController oglemock.Controller + + // A context that can be used by tests for long-running operations. In + // particular, this enables conveniently tracing the execution of a test + // function with reqtrace. + Ctx context.Context + + // A mutex protecting shared state. + mu sync.RWMutex + + // A set of failure records that the test has produced. + // + // GUARDED_BY(mu) + failureRecords []FailureRecord +} + +// currentlyRunningTest is the state for the currently running test, if any. +var currentlyRunningTest *TestInfo + +// newTestInfo creates a valid but empty TestInfo struct. +func newTestInfo() (info *TestInfo) { + info = &TestInfo{} + info.MockController = oglemock.NewController(&testInfoErrorReporter{info}) + info.Ctx = context.Background() + + return +} + +// testInfoErrorReporter is an oglemock.ErrorReporter that writes failure +// records into a test info struct. +type testInfoErrorReporter struct { + testInfo *TestInfo +} + +func (r *testInfoErrorReporter) ReportError( + fileName string, + lineNumber int, + err error) { + r.testInfo.mu.Lock() + defer r.testInfo.mu.Unlock() + + record := FailureRecord{ + FileName: fileName, + LineNumber: lineNumber, + Error: err.Error(), + } + + r.testInfo.failureRecords = append(r.testInfo.failureRecords, record) +} + +func (r *testInfoErrorReporter) ReportFatalError( + fileName string, + lineNumber int, + err error) { + r.ReportError(fileName, lineNumber, err) + AbortTest() +} diff --git a/vendor/github.com/smartystreets/assertions/internal/reqtrace/LICENSE b/vendor/github.com/smartystreets/assertions/internal/reqtrace/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/reqtrace/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/smartystreets/assertions/internal/reqtrace/README.md b/vendor/github.com/smartystreets/assertions/internal/reqtrace/README.md new file mode 100644 index 0000000..4392452 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/reqtrace/README.md @@ -0,0 +1,53 @@ +[![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/reqtrace?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/reqtrace) + +reqtrace is a package for simple request tracing. It requires nothing of its +user except: + + * They must use [golang.org/x/net/context][context]. + * They must add a single line to each function they want to be visible in + traces. + +[context]: http://godoc.org/golang.org/x/net/context + +In particular, reqtrace is console-based and doesn't require an HTTP server. + +**Warning**: This package is still barebones and in its early days. I reserve +the right to make backwards-incompatible changes to its API. But if it's useful +to you in your current form, have at it. + +## Use + +Call reqtrace.Trace anywhere you want to start a new root trace. (This is +probably where you create your root context.) This returns a new context that +you should pass to child operations, and a reporting function that you must use +to inform reqtrace when the trace is complete. + +For example: + +```Go +func HandleRequest(r *someRequest) (err error) { + ctx, report := reqtrace.Trace(context.Background(), "HandleRequest") + defer func() { report(err) }() + + // Do two things for this request. + DoSomething(ctx, r) + DoSomethingElse(ctx, r) +} +``` + +Within other functions that you want to show up in the trace, you +reqtrace.StartSpan (or its more convenient sibling reqtrace.StartSpanWithError): + +```Go +func DoSomething(ctx context.Context, r *someRequest) (err error) { + defer reqtrace.StartSpanWithError(&ctx, &err, "DoSomething")() + + // Process the request somehow using ctx. If downstream code also annotes + // using reqtrace, reqtrace will know that its spans are descendants of + // this one. + CallAnotherLibrary(ctx, r.Param) +} +``` + +When `--reqtrace.enable` is set, the completion of a trace will cause helpful +ASCII art to be spit out. diff --git a/vendor/github.com/smartystreets/assertions/internal/reqtrace/reqtrace.go b/vendor/github.com/smartystreets/assertions/internal/reqtrace/reqtrace.go new file mode 100644 index 0000000..b1267de --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/reqtrace/reqtrace.go @@ -0,0 +1,132 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package reqtrace contains a very simple request tracing framework. +package reqtrace + +import ( + "flag" + + "github.com/moul/ssh2docker/vendor/golang.org/x/net/context" +) + +type contextKey int + +var fEnabled = flag.Bool("reqtrace.enable", false, "Collect and print traces.") + +// The key used to associate a *traceState with a context. +const traceStateKey contextKey = 0 + +// A function that must be called exactly once to report the outcome of an +// operation represented by a span. +type ReportFunc func(error) + +// Return false only if traces are disabled, i.e. Trace will never cause a +// trace to be initiated. +// +// REQUIRES: flag.Parsed() +func Enabled() (enabled bool) { + enabled = *fEnabled + return +} + +// Begin a span within the current trace. Return a new context that should be +// used for operations that logically occur within the span, and a report +// function that must be called with the outcome of the logical operation +// represented by the span. +// +// If no trace is active, no span will be created but ctx and report will still +// be valid. +func StartSpan( + parent context.Context, + desc string) (ctx context.Context, report ReportFunc) { + // Look for the trace state. + val := parent.Value(traceStateKey) + if val == nil { + // Nothing to do. + ctx = parent + report = func(err error) {} + return + } + + ts := val.(*traceState) + + // Set up the report function. + report = ts.CreateSpan(desc) + + // For now we don't do anything interesting with the context. In the future, + // we may use it to record span hierarchy. + ctx = parent + + return +} + +// A wrapper around StartSpan that can be more convenient to use when the +// lifetime of a span matches the lifetime of a function. Intended to be used +// in a defer statement within a function using a named error return parameter. +// +// Equivalent to calling StartSpan with *ctx, replacing *ctx with the resulting +// new context, then setting f to a function that will invoke the report +// function with the contents of *error at the time that it is called. +// +// Example: +// +// func DoSomething(ctx context.Context) (err error) { +// defer reqtrace.StartSpanWithError(&ctx, &err, "DoSomething")() +// [...] +// } +// +func StartSpanWithError( + ctx *context.Context, + err *error, + desc string) (f func()) { + var report ReportFunc + *ctx, report = StartSpan(*ctx, desc) + f = func() { report(*err) } + return +} + +// Like StartSpan, but begins a root span for a new trace if no trace is active +// in the supplied context and tracing is enabled for the process. +func Trace( + parent context.Context, + desc string) (ctx context.Context, report ReportFunc) { + // If tracing is disabled, this is a no-op. + if !*fEnabled { + ctx = parent + report = func(err error) {} + return + } + + // Is this context already being traced? If so, simply add a span. + if parent.Value(traceStateKey) != nil { + ctx, report = StartSpan(parent, desc) + return + } + + // Set up a new trace state. + ts := new(traceState) + baseReport := ts.CreateSpan(desc) + + // Log when finished. + report = func(err error) { + baseReport(err) + ts.Log() + } + + // Set up the context. + ctx = context.WithValue(parent, traceStateKey, ts) + + return +} diff --git a/vendor/github.com/smartystreets/assertions/internal/reqtrace/trace_state.go b/vendor/github.com/smartystreets/assertions/internal/reqtrace/trace_state.go new file mode 100644 index 0000000..614ef90 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/internal/reqtrace/trace_state.go @@ -0,0 +1,175 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reqtrace + +import ( + "log" + "math" + "os" + "strings" + "sync" + "time" +) + +const logFlags = 0 + +var gLogger = log.New(os.Stderr, "reqtrace: ", logFlags) + +type span struct { + // Fixed at creation. + desc string + start time.Time + + // Updated by report functions. + finished bool + end time.Time + err error +} + +// All of the state for a particular trace root. The zero value is usable. +type traceState struct { + mu sync.Mutex + + // The list of spans associated with this state. Append-only. + // + // GUARDED_BY(mu) + spans []*span +} + +func (ts *traceState) report(spanIndex int, err error) { + ts.mu.Lock() + defer ts.mu.Unlock() + + s := ts.spans[spanIndex] + s.finished = true + s.end = time.Now() + s.err = err +} + +// Associate a new span with the trace. Return a function that will report its +// completion. +func (ts *traceState) CreateSpan(desc string) (report ReportFunc) { + ts.mu.Lock() + defer ts.mu.Unlock() + + index := len(ts.spans) + ts.spans = append(ts.spans, &span{desc: desc, start: time.Now()}) + + report = func(err error) { ts.report(index, err) } + return +} + +func round(x float64) float64 { + if x < 0 { + return math.Ceil(x - 0.5) + } + + return math.Floor(x + 0.5) +} + +// Log information about the spans in this trace. +func (ts *traceState) Log() { + ts.mu.Lock() + defer ts.mu.Unlock() + gLogger.Println() + + // Special case: we require at least one span. + if len(ts.spans) == 0 { + return + } + + // Print a banner for this trace. + const bannerHalfLength = 45 + + gLogger.Println() + gLogger.Printf( + "%s %s %s", + strings.Repeat("=", bannerHalfLength), + ts.spans[0].desc, + strings.Repeat("=", bannerHalfLength)) + gLogger.Printf("Start time: %v", ts.spans[0].start.Format(time.RFC3339Nano)) + gLogger.Println() + + // Find the minimum start time and maximum end time of all durations. + var minStart time.Time + var maxEnd time.Time + for _, s := range ts.spans { + if !s.finished { + continue + } + + if minStart.IsZero() || s.start.Before(minStart) { + minStart = s.start + } + + if maxEnd.Before(s.end) { + maxEnd = s.end + } + } + + // Bail out if something weird happened. + // + // TODO(jacobsa): Be more graceful. + totalDuration := maxEnd.Sub(minStart) + if minStart.IsZero() || maxEnd.IsZero() || totalDuration <= 0 { + gLogger.Println("(Weird trace)") + return + } + + // Calculate the number of nanoseconds elapsed, as a floating point number. + totalNs := float64(totalDuration / time.Nanosecond) + + // Log each span with some ASCII art showing its length relative to the + // total. + const totalNumCols float64 = 120 + for _, s := range ts.spans { + if !s.finished { + gLogger.Printf("(Unfinished: %s)", s.desc) + gLogger.Println() + continue + } + + // Calculate the duration of the span, and its width relative to the + // longest span. + d := s.end.Sub(s.start) + if d <= 0 { + gLogger.Println("(Weird duration)") + gLogger.Println() + continue + } + + durationRatio := float64(d/time.Nanosecond) / totalNs + + // We will offset the label and banner proportional to the time since the + // start of the earliest span. + offsetRatio := float64(s.start.Sub(minStart)/time.Nanosecond) / totalNs + offsetChars := int(round(offsetRatio * totalNumCols)) + offsetStr := strings.Repeat(" ", offsetChars) + + // Print the description and duration. + gLogger.Printf("%s%v", offsetStr, s.desc) + gLogger.Printf("%s%v", offsetStr, d) + + // Print a banner showing the duration graphically. + bannerChars := int(round(durationRatio * totalNumCols)) + var dashes string + if bannerChars > 2 { + dashes = strings.Repeat("-", bannerChars-2) + } + + gLogger.Printf("%s|%s|", offsetStr, dashes) + gLogger.Println() + } +} diff --git a/vendor/github.com/smartystreets/assertions/messages.go b/vendor/github.com/smartystreets/assertions/messages.go new file mode 100644 index 0000000..e88906c --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/messages.go @@ -0,0 +1,89 @@ +package assertions + +const ( // equality + shouldHaveBeenEqual = "Expected: '%v'\nActual: '%v'\n(Should be equal)" + shouldNotHaveBeenEqual = "Expected '%v'\nto NOT equal '%v'\n(but it did)!" + shouldHaveBeenAlmostEqual = "Expected '%v' to almost equal '%v' (but it didn't)!" + shouldHaveNotBeenAlmostEqual = "Expected '%v' to NOT almost equal '%v' (but it did)!" + shouldHaveResembled = "Expected: '%#v'\nActual: '%#v'\n(Should resemble)!" + shouldHaveResembledTypeMismatch = "Expected: '%#v'\nActual: '%#v'\n(Type mismatch: '%T' vs '%T')!" + shouldNotHaveResembled = "Expected '%#v'\nto NOT resemble '%#v'\n(but it did)!" + shouldBePointers = "Both arguments should be pointers " + shouldHaveBeenNonNilPointer = shouldBePointers + "(the %s was %s)!" + shouldHavePointedTo = "Expected '%+v' (address: '%v') and '%+v' (address: '%v') to be the same address (but their weren't)!" + shouldNotHavePointedTo = "Expected '%+v' and '%+v' to be different references (but they matched: '%v')!" + shouldHaveBeenNil = "Expected: nil\nActual: '%v'" + shouldNotHaveBeenNil = "Expected '%+v' to NOT be nil (but it was)!" + shouldHaveBeenTrue = "Expected: true\nActual: %v" + shouldHaveBeenFalse = "Expected: false\nActual: %v" + shouldHaveBeenZeroValue = "'%+v' should have been the zero value" //"Expected: (zero value)\nActual: %v" +) + +const ( // quantity comparisons + shouldHaveBeenGreater = "Expected '%v' to be greater than '%v' (but it wasn't)!" + shouldHaveBeenGreaterOrEqual = "Expected '%v' to be greater than or equal to '%v' (but it wasn't)!" + shouldHaveBeenLess = "Expected '%v' to be less than '%v' (but it wasn't)!" + shouldHaveBeenLessOrEqual = "Expected '%v' to be less than or equal to '%v' (but it wasn't)!" + shouldHaveBeenBetween = "Expected '%v' to be between '%v' and '%v' (but it wasn't)!" + shouldNotHaveBeenBetween = "Expected '%v' NOT to be between '%v' and '%v' (but it was)!" + shouldHaveDifferentUpperAndLower = "The lower and upper bounds must be different values (they were both '%v')." + shouldHaveBeenBetweenOrEqual = "Expected '%v' to be between '%v' and '%v' or equal to one of them (but it wasn't)!" + shouldNotHaveBeenBetweenOrEqual = "Expected '%v' NOT to be between '%v' and '%v' or equal to one of them (but it was)!" +) + +const ( // collections + shouldHaveContained = "Expected the container (%v) to contain: '%v' (but it didn't)!" + shouldNotHaveContained = "Expected the container (%v) NOT to contain: '%v' (but it did)!" + shouldHaveContainedKey = "Expected the %v to contain the key: %v (but it didn't)!" + shouldNotHaveContainedKey = "Expected the %v NOT to contain the key: %v (but it did)!" + shouldHaveBeenIn = "Expected '%v' to be in the container (%v), but it wasn't!" + shouldNotHaveBeenIn = "Expected '%v' NOT to be in the container (%v), but it was!" + shouldHaveBeenAValidCollection = "You must provide a valid container (was %v)!" + shouldHaveBeenAValidMap = "You must provide a valid map type (was %v)!" + shouldHaveBeenEmpty = "Expected %+v to be empty (but it wasn't)!" + shouldNotHaveBeenEmpty = "Expected %+v to NOT be empty (but it was)!" +) + +const ( // strings + shouldHaveStartedWith = "Expected '%v'\nto start with '%v'\n(but it didn't)!" + shouldNotHaveStartedWith = "Expected '%v'\nNOT to start with '%v'\n(but it did)!" + shouldHaveEndedWith = "Expected '%v'\nto end with '%v'\n(but it didn't)!" + shouldNotHaveEndedWith = "Expected '%v'\nNOT to end with '%v'\n(but it did)!" + shouldBothBeStrings = "Both arguments to this assertion must be strings (you provided %v and %v)." + shouldBeString = "The argument to this assertion must be a string (you provided %v)." + shouldHaveContainedSubstring = "Expected '%s' to contain substring '%s' (but it didn't)!" + shouldNotHaveContainedSubstring = "Expected '%s' NOT to contain substring '%s' (but it did)!" + shouldHaveBeenBlank = "Expected '%s' to be blank (but it wasn't)!" + shouldNotHaveBeenBlank = "Expected value to NOT be blank (but it was)!" +) + +const ( // panics + shouldUseVoidNiladicFunction = "You must provide a void, niladic function as the first argument!" + shouldHavePanickedWith = "Expected func() to panic with '%v' (but it panicked with '%v')!" + shouldHavePanicked = "Expected func() to panic (but it didn't)!" + shouldNotHavePanicked = "Expected func() NOT to panic (error: '%+v')!" + shouldNotHavePanickedWith = "Expected func() NOT to panic with '%v' (but it did)!" +) + +const ( // type checking + shouldHaveBeenA = "Expected '%v' to be: '%v' (but was: '%v')!" + shouldNotHaveBeenA = "Expected '%v' to NOT be: '%v' (but it was)!" + + shouldHaveImplemented = "Expected: '%v interface support'\nActual: '%v' does not implement the interface!" + shouldNotHaveImplemented = "Expected '%v'\nto NOT implement '%v'\n(but it did)!" + shouldCompareWithInterfacePointer = "The expected value must be a pointer to an interface type (eg. *fmt.Stringer)" + shouldNotBeNilActual = "The actual value was 'nil' and should be a value or a pointer to a value!" +) + +const ( // time comparisons + shouldUseTimes = "You must provide time instances as arguments to this assertion." + shouldUseTimeSlice = "You must provide a slice of time instances as the first argument to this assertion." + shouldUseDurationAndTime = "You must provide a duration and a time as arguments to this assertion." + shouldHaveHappenedBefore = "Expected '%v' to happen before '%v' (it happened '%v' after)!" + shouldHaveHappenedAfter = "Expected '%v' to happen after '%v' (it happened '%v' before)!" + shouldHaveHappenedBetween = "Expected '%v' to happen between '%v' and '%v' (it happened '%v' outside threshold)!" + shouldNotHaveHappenedOnOrBetween = "Expected '%v' to NOT happen on or between '%v' and '%v' (but it did)!" + + // format params: incorrect-index, previous-index, previous-time, incorrect-index, incorrect-time + shouldHaveBeenChronological = "The 'Time' at index [%d] should have happened after the previous one (but it didn't!):\n [%d]: %s\n [%d]: %s (see, it happened before!)" +) diff --git a/vendor/github.com/smartystreets/assertions/panic.go b/vendor/github.com/smartystreets/assertions/panic.go new file mode 100644 index 0000000..7e75db1 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/panic.go @@ -0,0 +1,115 @@ +package assertions + +import "fmt" + +// ShouldPanic receives a void, niladic function and expects to recover a panic. +func ShouldPanic(actual interface{}, expected ...interface{}) (message string) { + if fail := need(0, expected); fail != success { + return fail + } + + action, _ := actual.(func()) + + if action == nil { + message = shouldUseVoidNiladicFunction + return + } + + defer func() { + recovered := recover() + if recovered == nil { + message = shouldHavePanicked + } else { + message = success + } + }() + action() + + return +} + +// ShouldNotPanic receives a void, niladic function and expects to execute the function without any panic. +func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string) { + if fail := need(0, expected); fail != success { + return fail + } + + action, _ := actual.(func()) + + if action == nil { + message = shouldUseVoidNiladicFunction + return + } + + defer func() { + recovered := recover() + if recovered != nil { + message = fmt.Sprintf(shouldNotHavePanicked, recovered) + } else { + message = success + } + }() + action() + + return +} + +// ShouldPanicWith receives a void, niladic function and expects to recover a panic with the second argument as the content. +func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string) { + if fail := need(1, expected); fail != success { + return fail + } + + action, _ := actual.(func()) + + if action == nil { + message = shouldUseVoidNiladicFunction + return + } + + defer func() { + recovered := recover() + if recovered == nil { + message = shouldHavePanicked + } else { + if equal := ShouldEqual(recovered, expected[0]); equal != success { + message = serializer.serialize(expected[0], recovered, fmt.Sprintf(shouldHavePanickedWith, expected[0], recovered)) + } else { + message = success + } + } + }() + action() + + return +} + +// ShouldNotPanicWith receives a void, niladic function and expects to recover a panic whose content differs from the second argument. +func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string) { + if fail := need(1, expected); fail != success { + return fail + } + + action, _ := actual.(func()) + + if action == nil { + message = shouldUseVoidNiladicFunction + return + } + + defer func() { + recovered := recover() + if recovered == nil { + message = success + } else { + if equal := ShouldEqual(recovered, expected[0]); equal == success { + message = fmt.Sprintf(shouldNotHavePanickedWith, expected[0]) + } else { + message = success + } + } + }() + action() + + return +} diff --git a/vendor/github.com/smartystreets/assertions/panic_test.go b/vendor/github.com/smartystreets/assertions/panic_test.go new file mode 100644 index 0000000..15eafac --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/panic_test.go @@ -0,0 +1,53 @@ +package assertions + +import ( + "fmt" + "testing" +) + +func TestShouldPanic(t *testing.T) { + fail(t, so(func() {}, ShouldPanic, 1), "This assertion requires exactly 0 comparison values (you provided 1).") + fail(t, so(func() {}, ShouldPanic, 1, 2, 3), "This assertion requires exactly 0 comparison values (you provided 3).") + + fail(t, so(1, ShouldPanic), shouldUseVoidNiladicFunction) + fail(t, so(func(i int) {}, ShouldPanic), shouldUseVoidNiladicFunction) + fail(t, so(func() int { panic("hi") }, ShouldPanic), shouldUseVoidNiladicFunction) + + fail(t, so(func() {}, ShouldPanic), shouldHavePanicked) + pass(t, so(func() { panic("hi") }, ShouldPanic)) +} + +func TestShouldNotPanic(t *testing.T) { + fail(t, so(func() {}, ShouldNotPanic, 1), "This assertion requires exactly 0 comparison values (you provided 1).") + fail(t, so(func() {}, ShouldNotPanic, 1, 2, 3), "This assertion requires exactly 0 comparison values (you provided 3).") + + fail(t, so(1, ShouldNotPanic), shouldUseVoidNiladicFunction) + fail(t, so(func(i int) {}, ShouldNotPanic), shouldUseVoidNiladicFunction) + + fail(t, so(func() { panic("hi") }, ShouldNotPanic), fmt.Sprintf(shouldNotHavePanicked, "hi")) + pass(t, so(func() {}, ShouldNotPanic)) +} + +func TestShouldPanicWith(t *testing.T) { + fail(t, so(func() {}, ShouldPanicWith), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(func() {}, ShouldPanicWith, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(1, ShouldPanicWith, 1), shouldUseVoidNiladicFunction) + fail(t, so(func(i int) {}, ShouldPanicWith, "hi"), shouldUseVoidNiladicFunction) + fail(t, so(func() {}, ShouldPanicWith, "bye"), shouldHavePanicked) + fail(t, so(func() { panic("hi") }, ShouldPanicWith, "bye"), "bye|hi|Expected func() to panic with 'bye' (but it panicked with 'hi')!") + + pass(t, so(func() { panic("hi") }, ShouldPanicWith, "hi")) +} + +func TestShouldNotPanicWith(t *testing.T) { + fail(t, so(func() {}, ShouldNotPanicWith), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(func() {}, ShouldNotPanicWith, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(1, ShouldNotPanicWith, 1), shouldUseVoidNiladicFunction) + fail(t, so(func(i int) {}, ShouldNotPanicWith, "hi"), shouldUseVoidNiladicFunction) + fail(t, so(func() { panic("hi") }, ShouldNotPanicWith, "hi"), "Expected func() NOT to panic with 'hi' (but it did)!") + + pass(t, so(func() {}, ShouldNotPanicWith, "bye")) + pass(t, so(func() { panic("hi") }, ShouldNotPanicWith, "bye")) +} diff --git a/vendor/github.com/smartystreets/assertions/quantity.go b/vendor/github.com/smartystreets/assertions/quantity.go new file mode 100644 index 0000000..dd75afb --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/quantity.go @@ -0,0 +1,141 @@ +package assertions + +import ( + "fmt" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions/internal/oglematchers" +) + +// ShouldBeGreaterThan receives exactly two parameters and ensures that the first is greater than the second. +func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + if matchError := oglematchers.GreaterThan(expected[0]).Matches(actual); matchError != nil { + return fmt.Sprintf(shouldHaveBeenGreater, actual, expected[0]) + } + return success +} + +// ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that the first is greater than or equal to the second. +func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } else if matchError := oglematchers.GreaterOrEqual(expected[0]).Matches(actual); matchError != nil { + return fmt.Sprintf(shouldHaveBeenGreaterOrEqual, actual, expected[0]) + } + return success +} + +// ShouldBeLessThan receives exactly two parameters and ensures that the first is less than the second. +func ShouldBeLessThan(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } else if matchError := oglematchers.LessThan(expected[0]).Matches(actual); matchError != nil { + return fmt.Sprintf(shouldHaveBeenLess, actual, expected[0]) + } + return success +} + +// ShouldBeLessThan receives exactly two parameters and ensures that the first is less than or equal to the second. +func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } else if matchError := oglematchers.LessOrEqual(expected[0]).Matches(actual); matchError != nil { + return fmt.Sprintf(shouldHaveBeenLess, actual, expected[0]) + } + return success +} + +// ShouldBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound. +// It ensures that the actual value is between both bounds (but not equal to either of them). +func ShouldBeBetween(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + lower, upper, fail := deriveBounds(expected) + + if fail != success { + return fail + } else if !isBetween(actual, lower, upper) { + return fmt.Sprintf(shouldHaveBeenBetween, actual, lower, upper) + } + return success +} + +// ShouldNotBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound. +// It ensures that the actual value is NOT between both bounds. +func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + lower, upper, fail := deriveBounds(expected) + + if fail != success { + return fail + } else if isBetween(actual, lower, upper) { + return fmt.Sprintf(shouldNotHaveBeenBetween, actual, lower, upper) + } + return success +} +func deriveBounds(values []interface{}) (lower interface{}, upper interface{}, fail string) { + lower = values[0] + upper = values[1] + + if ShouldNotEqual(lower, upper) != success { + return nil, nil, fmt.Sprintf(shouldHaveDifferentUpperAndLower, lower) + } else if ShouldBeLessThan(lower, upper) != success { + lower, upper = upper, lower + } + return lower, upper, success +} +func isBetween(value, lower, upper interface{}) bool { + if ShouldBeGreaterThan(value, lower) != success { + return false + } else if ShouldBeLessThan(value, upper) != success { + return false + } + return true +} + +// ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound. +// It ensures that the actual value is between both bounds or equal to one of them. +func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + lower, upper, fail := deriveBounds(expected) + + if fail != success { + return fail + } else if !isBetweenOrEqual(actual, lower, upper) { + return fmt.Sprintf(shouldHaveBeenBetweenOrEqual, actual, lower, upper) + } + return success +} + +// ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound. +// It ensures that the actual value is nopt between the bounds nor equal to either of them. +func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + lower, upper, fail := deriveBounds(expected) + + if fail != success { + return fail + } else if isBetweenOrEqual(actual, lower, upper) { + return fmt.Sprintf(shouldNotHaveBeenBetweenOrEqual, actual, lower, upper) + } + return success +} + +func isBetweenOrEqual(value, lower, upper interface{}) bool { + if ShouldBeGreaterThanOrEqualTo(value, lower) != success { + return false + } else if ShouldBeLessThanOrEqualTo(value, upper) != success { + return false + } + return true +} diff --git a/vendor/github.com/smartystreets/assertions/quantity_test.go b/vendor/github.com/smartystreets/assertions/quantity_test.go new file mode 100644 index 0000000..7546e72 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/quantity_test.go @@ -0,0 +1,145 @@ +package assertions + +import "testing" + +func TestShouldBeGreaterThan(t *testing.T) { + fail(t, so(1, ShouldBeGreaterThan), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldBeGreaterThan, 0, 0), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(1, ShouldBeGreaterThan, 0)) + pass(t, so(1.1, ShouldBeGreaterThan, 1)) + pass(t, so(1, ShouldBeGreaterThan, uint(0))) + pass(t, so("b", ShouldBeGreaterThan, "a")) + + fail(t, so(0, ShouldBeGreaterThan, 1), "Expected '0' to be greater than '1' (but it wasn't)!") + fail(t, so(1, ShouldBeGreaterThan, 1.1), "Expected '1' to be greater than '1.1' (but it wasn't)!") + fail(t, so(uint(0), ShouldBeGreaterThan, 1.1), "Expected '0' to be greater than '1.1' (but it wasn't)!") + fail(t, so("a", ShouldBeGreaterThan, "b"), "Expected 'a' to be greater than 'b' (but it wasn't)!") +} + +func TestShouldBeGreaterThanOrEqual(t *testing.T) { + fail(t, so(1, ShouldBeGreaterThanOrEqualTo), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldBeGreaterThanOrEqualTo, 0, 0), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(1, ShouldBeGreaterThanOrEqualTo, 1)) + pass(t, so(1.1, ShouldBeGreaterThanOrEqualTo, 1.1)) + pass(t, so(1, ShouldBeGreaterThanOrEqualTo, uint(1))) + pass(t, so("b", ShouldBeGreaterThanOrEqualTo, "b")) + + pass(t, so(1, ShouldBeGreaterThanOrEqualTo, 0)) + pass(t, so(1.1, ShouldBeGreaterThanOrEqualTo, 1)) + pass(t, so(1, ShouldBeGreaterThanOrEqualTo, uint(0))) + pass(t, so("b", ShouldBeGreaterThanOrEqualTo, "a")) + + fail(t, so(0, ShouldBeGreaterThanOrEqualTo, 1), "Expected '0' to be greater than or equal to '1' (but it wasn't)!") + fail(t, so(1, ShouldBeGreaterThanOrEqualTo, 1.1), "Expected '1' to be greater than or equal to '1.1' (but it wasn't)!") + fail(t, so(uint(0), ShouldBeGreaterThanOrEqualTo, 1.1), "Expected '0' to be greater than or equal to '1.1' (but it wasn't)!") + fail(t, so("a", ShouldBeGreaterThanOrEqualTo, "b"), "Expected 'a' to be greater than or equal to 'b' (but it wasn't)!") +} + +func TestShouldBeLessThan(t *testing.T) { + fail(t, so(1, ShouldBeLessThan), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldBeLessThan, 0, 0), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(0, ShouldBeLessThan, 1)) + pass(t, so(1, ShouldBeLessThan, 1.1)) + pass(t, so(uint(0), ShouldBeLessThan, 1)) + pass(t, so("a", ShouldBeLessThan, "b")) + + fail(t, so(1, ShouldBeLessThan, 0), "Expected '1' to be less than '0' (but it wasn't)!") + fail(t, so(1.1, ShouldBeLessThan, 1), "Expected '1.1' to be less than '1' (but it wasn't)!") + fail(t, so(1.1, ShouldBeLessThan, uint(0)), "Expected '1.1' to be less than '0' (but it wasn't)!") + fail(t, so("b", ShouldBeLessThan, "a"), "Expected 'b' to be less than 'a' (but it wasn't)!") +} + +func TestShouldBeLessThanOrEqualTo(t *testing.T) { + fail(t, so(1, ShouldBeLessThanOrEqualTo), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldBeLessThanOrEqualTo, 0, 0), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so(1, ShouldBeLessThanOrEqualTo, 1)) + pass(t, so(1.1, ShouldBeLessThanOrEqualTo, 1.1)) + pass(t, so(uint(1), ShouldBeLessThanOrEqualTo, 1)) + pass(t, so("b", ShouldBeLessThanOrEqualTo, "b")) + + pass(t, so(0, ShouldBeLessThanOrEqualTo, 1)) + pass(t, so(1, ShouldBeLessThanOrEqualTo, 1.1)) + pass(t, so(uint(0), ShouldBeLessThanOrEqualTo, 1)) + pass(t, so("a", ShouldBeLessThanOrEqualTo, "b")) + + fail(t, so(1, ShouldBeLessThanOrEqualTo, 0), "Expected '1' to be less than '0' (but it wasn't)!") + fail(t, so(1.1, ShouldBeLessThanOrEqualTo, 1), "Expected '1.1' to be less than '1' (but it wasn't)!") + fail(t, so(1.1, ShouldBeLessThanOrEqualTo, uint(0)), "Expected '1.1' to be less than '0' (but it wasn't)!") + fail(t, so("b", ShouldBeLessThanOrEqualTo, "a"), "Expected 'b' to be less than 'a' (but it wasn't)!") +} + +func TestShouldBeBetween(t *testing.T) { + fail(t, so(1, ShouldBeBetween), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(1, ShouldBeBetween, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(4, ShouldBeBetween, 1, 1), "The lower and upper bounds must be different values (they were both '1').") + + fail(t, so(7, ShouldBeBetween, 8, 12), "Expected '7' to be between '8' and '12' (but it wasn't)!") + fail(t, so(8, ShouldBeBetween, 8, 12), "Expected '8' to be between '8' and '12' (but it wasn't)!") + pass(t, so(9, ShouldBeBetween, 8, 12)) + pass(t, so(10, ShouldBeBetween, 8, 12)) + pass(t, so(11, ShouldBeBetween, 8, 12)) + fail(t, so(12, ShouldBeBetween, 8, 12), "Expected '12' to be between '8' and '12' (but it wasn't)!") + fail(t, so(13, ShouldBeBetween, 8, 12), "Expected '13' to be between '8' and '12' (but it wasn't)!") + + pass(t, so(1, ShouldBeBetween, 2, 0)) + fail(t, so(-1, ShouldBeBetween, 2, 0), "Expected '-1' to be between '0' and '2' (but it wasn't)!") +} + +func TestShouldNotBeBetween(t *testing.T) { + fail(t, so(1, ShouldNotBeBetween), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(1, ShouldNotBeBetween, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(4, ShouldNotBeBetween, 1, 1), "The lower and upper bounds must be different values (they were both '1').") + + pass(t, so(7, ShouldNotBeBetween, 8, 12)) + pass(t, so(8, ShouldNotBeBetween, 8, 12)) + fail(t, so(9, ShouldNotBeBetween, 8, 12), "Expected '9' NOT to be between '8' and '12' (but it was)!") + fail(t, so(10, ShouldNotBeBetween, 8, 12), "Expected '10' NOT to be between '8' and '12' (but it was)!") + fail(t, so(11, ShouldNotBeBetween, 8, 12), "Expected '11' NOT to be between '8' and '12' (but it was)!") + pass(t, so(12, ShouldNotBeBetween, 8, 12)) + pass(t, so(13, ShouldNotBeBetween, 8, 12)) + + pass(t, so(-1, ShouldNotBeBetween, 2, 0)) + fail(t, so(1, ShouldNotBeBetween, 2, 0), "Expected '1' NOT to be between '0' and '2' (but it was)!") +} + +func TestShouldBeBetweenOrEqual(t *testing.T) { + fail(t, so(1, ShouldBeBetweenOrEqual), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(1, ShouldBeBetweenOrEqual, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(4, ShouldBeBetweenOrEqual, 1, 1), "The lower and upper bounds must be different values (they were both '1').") + + fail(t, so(7, ShouldBeBetweenOrEqual, 8, 12), "Expected '7' to be between '8' and '12' or equal to one of them (but it wasn't)!") + pass(t, so(8, ShouldBeBetweenOrEqual, 8, 12)) + pass(t, so(9, ShouldBeBetweenOrEqual, 8, 12)) + pass(t, so(10, ShouldBeBetweenOrEqual, 8, 12)) + pass(t, so(11, ShouldBeBetweenOrEqual, 8, 12)) + pass(t, so(12, ShouldBeBetweenOrEqual, 8, 12)) + fail(t, so(13, ShouldBeBetweenOrEqual, 8, 12), "Expected '13' to be between '8' and '12' or equal to one of them (but it wasn't)!") + + pass(t, so(1, ShouldBeBetweenOrEqual, 2, 0)) + fail(t, so(-1, ShouldBeBetweenOrEqual, 2, 0), "Expected '-1' to be between '0' and '2' or equal to one of them (but it wasn't)!") +} + +func TestShouldNotBeBetweenOrEqual(t *testing.T) { + fail(t, so(1, ShouldNotBeBetweenOrEqual), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(1, ShouldNotBeBetweenOrEqual, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(4, ShouldNotBeBetweenOrEqual, 1, 1), "The lower and upper bounds must be different values (they were both '1').") + + pass(t, so(7, ShouldNotBeBetweenOrEqual, 8, 12)) + fail(t, so(8, ShouldNotBeBetweenOrEqual, 8, 12), "Expected '8' NOT to be between '8' and '12' or equal to one of them (but it was)!") + fail(t, so(9, ShouldNotBeBetweenOrEqual, 8, 12), "Expected '9' NOT to be between '8' and '12' or equal to one of them (but it was)!") + fail(t, so(10, ShouldNotBeBetweenOrEqual, 8, 12), "Expected '10' NOT to be between '8' and '12' or equal to one of them (but it was)!") + fail(t, so(11, ShouldNotBeBetweenOrEqual, 8, 12), "Expected '11' NOT to be between '8' and '12' or equal to one of them (but it was)!") + fail(t, so(12, ShouldNotBeBetweenOrEqual, 8, 12), "Expected '12' NOT to be between '8' and '12' or equal to one of them (but it was)!") + pass(t, so(13, ShouldNotBeBetweenOrEqual, 8, 12)) + + pass(t, so(-1, ShouldNotBeBetweenOrEqual, 2, 0)) + fail(t, so(1, ShouldNotBeBetweenOrEqual, 2, 0), "Expected '1' NOT to be between '0' and '2' or equal to one of them (but it was)!") +} diff --git a/vendor/github.com/smartystreets/assertions/serializer.go b/vendor/github.com/smartystreets/assertions/serializer.go new file mode 100644 index 0000000..f55cc25 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/serializer.go @@ -0,0 +1,59 @@ +package assertions + +import ( + "encoding/json" + "fmt" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" +) + +type Serializer interface { + serialize(expected, actual interface{}, message string) string + serializeDetailed(expected, actual interface{}, message string) string +} + +type failureSerializer struct{} + +func (self *failureSerializer) serializeDetailed(expected, actual interface{}, message string) string { + view := self.format(expected, actual, message, "%#v") + serialized, err := json.Marshal(view) + if err != nil { + return message + } + return string(serialized) +} + +func (self *failureSerializer) serialize(expected, actual interface{}, message string) string { + view := self.format(expected, actual, message, "%+v") + serialized, err := json.Marshal(view) + if err != nil { + return message + } + return string(serialized) +} + +func (self *failureSerializer) format(expected, actual interface{}, message string, format string) reporting.FailureView { + return reporting.FailureView{ + Message: message, + Expected: fmt.Sprintf(format, expected), + Actual: fmt.Sprintf(format, actual), + } +} + +func newSerializer() *failureSerializer { + return &failureSerializer{} +} + +/////////////////////////////////////////////////////// + +// noopSerializer just gives back the original message. This is useful when we are using +// the assertions from a context other than the web UI, that requires the JSON structure +// provided by the failureSerializer. +type noopSerializer struct{} + +func (self *noopSerializer) serialize(expected, actual interface{}, message string) string { + return message +} +func (self *noopSerializer) serializeDetailed(expected, actual interface{}, message string) string { + return message +} diff --git a/vendor/github.com/smartystreets/assertions/serializer_test.go b/vendor/github.com/smartystreets/assertions/serializer_test.go new file mode 100644 index 0000000..b32dc78 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/serializer_test.go @@ -0,0 +1,38 @@ +package assertions + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" +) + +func TestSerializerCreatesSerializedVersionOfAssertionResult(t *testing.T) { + thing1 := Thing1{"Hi"} + thing2 := Thing2{"Bye"} + message := "Super-hip failure message." + serializer := newSerializer() + + actualResult := serializer.serialize(thing1, thing2, message) + + expectedResult, _ := json.Marshal(reporting.FailureView{ + Message: message, + Expected: fmt.Sprintf("%+v", thing1), + Actual: fmt.Sprintf("%+v", thing2), + }) + + if actualResult != string(expectedResult) { + t.Errorf("\nExpected: %s\nActual: %s", string(expectedResult), actualResult) + } + + actualResult = serializer.serializeDetailed(thing1, thing2, message) + expectedResult, _ = json.Marshal(reporting.FailureView{ + Message: message, + Expected: fmt.Sprintf("%#v", thing1), + Actual: fmt.Sprintf("%#v", thing2), + }) + if actualResult != string(expectedResult) { + t.Errorf("\nExpected: %s\nActual: %s", string(expectedResult), actualResult) + } +} diff --git a/vendor/github.com/smartystreets/assertions/strings.go b/vendor/github.com/smartystreets/assertions/strings.go new file mode 100644 index 0000000..1b887b1 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/strings.go @@ -0,0 +1,183 @@ +package assertions + +import ( + "fmt" + "reflect" + "strings" +) + +// ShouldStartWith receives exactly 2 string parameters and ensures that the first starts with the second. +func ShouldStartWith(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + value, valueIsString := actual.(string) + prefix, prefixIsString := expected[0].(string) + + if !valueIsString || !prefixIsString { + return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0])) + } + + return shouldStartWith(value, prefix) +} +func shouldStartWith(value, prefix string) string { + if !strings.HasPrefix(value, prefix) { + shortval := value + if len(shortval) > len(prefix) { + shortval = shortval[:len(prefix)] + "..." + } + return serializer.serialize(prefix, shortval, fmt.Sprintf(shouldHaveStartedWith, value, prefix)) + } + return success +} + +// ShouldNotStartWith receives exactly 2 string parameters and ensures that the first does not start with the second. +func ShouldNotStartWith(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + value, valueIsString := actual.(string) + prefix, prefixIsString := expected[0].(string) + + if !valueIsString || !prefixIsString { + return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0])) + } + + return shouldNotStartWith(value, prefix) +} +func shouldNotStartWith(value, prefix string) string { + if strings.HasPrefix(value, prefix) { + if value == "" { + value = "" + } + if prefix == "" { + prefix = "" + } + return fmt.Sprintf(shouldNotHaveStartedWith, value, prefix) + } + return success +} + +// ShouldEndWith receives exactly 2 string parameters and ensures that the first ends with the second. +func ShouldEndWith(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + value, valueIsString := actual.(string) + suffix, suffixIsString := expected[0].(string) + + if !valueIsString || !suffixIsString { + return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0])) + } + + return shouldEndWith(value, suffix) +} +func shouldEndWith(value, suffix string) string { + if !strings.HasSuffix(value, suffix) { + shortval := value + if len(shortval) > len(suffix) { + shortval = "..." + shortval[len(shortval)-len(suffix):] + } + return serializer.serialize(suffix, shortval, fmt.Sprintf(shouldHaveEndedWith, value, suffix)) + } + return success +} + +// ShouldEndWith receives exactly 2 string parameters and ensures that the first does not end with the second. +func ShouldNotEndWith(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + value, valueIsString := actual.(string) + suffix, suffixIsString := expected[0].(string) + + if !valueIsString || !suffixIsString { + return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0])) + } + + return shouldNotEndWith(value, suffix) +} +func shouldNotEndWith(value, suffix string) string { + if strings.HasSuffix(value, suffix) { + if value == "" { + value = "" + } + if suffix == "" { + suffix = "" + } + return fmt.Sprintf(shouldNotHaveEndedWith, value, suffix) + } + return success +} + +// ShouldContainSubstring receives exactly 2 string parameters and ensures that the first contains the second as a substring. +func ShouldContainSubstring(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + long, longOk := actual.(string) + short, shortOk := expected[0].(string) + + if !longOk || !shortOk { + return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0])) + } + + if !strings.Contains(long, short) { + return serializer.serialize(expected[0], actual, fmt.Sprintf(shouldHaveContainedSubstring, long, short)) + } + return success +} + +// ShouldNotContainSubstring receives exactly 2 string parameters and ensures that the first does NOT contain the second as a substring. +func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + long, longOk := actual.(string) + short, shortOk := expected[0].(string) + + if !longOk || !shortOk { + return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0])) + } + + if strings.Contains(long, short) { + return fmt.Sprintf(shouldNotHaveContainedSubstring, long, short) + } + return success +} + +// ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal to "". +func ShouldBeBlank(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } + value, ok := actual.(string) + if !ok { + return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual)) + } + if value != "" { + return serializer.serialize("", value, fmt.Sprintf(shouldHaveBeenBlank, value)) + } + return success +} + +// ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is equal to "". +func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } + value, ok := actual.(string) + if !ok { + return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual)) + } + if value == "" { + return shouldNotHaveBeenBlank + } + return success +} diff --git a/vendor/github.com/smartystreets/assertions/strings_test.go b/vendor/github.com/smartystreets/assertions/strings_test.go new file mode 100644 index 0000000..8341733 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/strings_test.go @@ -0,0 +1,102 @@ +package assertions + +import "testing" + +func TestShouldStartWith(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so("", ShouldStartWith), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so("", ShouldStartWith, "asdf", "asdf"), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so("", ShouldStartWith, "")) + fail(t, so("", ShouldStartWith, "x"), "x||Expected '' to start with 'x' (but it didn't)!") + pass(t, so("abc", ShouldStartWith, "abc")) + fail(t, so("abc", ShouldStartWith, "abcd"), "abcd|abc|Expected 'abc' to start with 'abcd' (but it didn't)!") + + pass(t, so("superman", ShouldStartWith, "super")) + fail(t, so("superman", ShouldStartWith, "bat"), "bat|sup...|Expected 'superman' to start with 'bat' (but it didn't)!") + fail(t, so("superman", ShouldStartWith, "man"), "man|sup...|Expected 'superman' to start with 'man' (but it didn't)!") + + fail(t, so(1, ShouldStartWith, 2), "Both arguments to this assertion must be strings (you provided int and int).") +} + +func TestShouldNotStartWith(t *testing.T) { + fail(t, so("", ShouldNotStartWith), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so("", ShouldNotStartWith, "asdf", "asdf"), "This assertion requires exactly 1 comparison values (you provided 2).") + + fail(t, so("", ShouldNotStartWith, ""), "Expected '' NOT to start with '' (but it did)!") + fail(t, so("superman", ShouldNotStartWith, "super"), "Expected 'superman' NOT to start with 'super' (but it did)!") + pass(t, so("superman", ShouldNotStartWith, "bat")) + pass(t, so("superman", ShouldNotStartWith, "man")) + + fail(t, so(1, ShouldNotStartWith, 2), "Both arguments to this assertion must be strings (you provided int and int).") +} + +func TestShouldEndWith(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so("", ShouldEndWith), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so("", ShouldEndWith, "", ""), "This assertion requires exactly 1 comparison values (you provided 2).") + + pass(t, so("", ShouldEndWith, "")) + fail(t, so("", ShouldEndWith, "z"), "z||Expected '' to end with 'z' (but it didn't)!") + pass(t, so("xyz", ShouldEndWith, "xyz")) + fail(t, so("xyz", ShouldEndWith, "wxyz"), "wxyz|xyz|Expected 'xyz' to end with 'wxyz' (but it didn't)!") + + pass(t, so("superman", ShouldEndWith, "man")) + fail(t, so("superman", ShouldEndWith, "super"), "super|...erman|Expected 'superman' to end with 'super' (but it didn't)!") + fail(t, so("superman", ShouldEndWith, "blah"), "blah|...rman|Expected 'superman' to end with 'blah' (but it didn't)!") + + fail(t, so(1, ShouldEndWith, 2), "Both arguments to this assertion must be strings (you provided int and int).") +} + +func TestShouldNotEndWith(t *testing.T) { + fail(t, so("", ShouldNotEndWith), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so("", ShouldNotEndWith, "", ""), "This assertion requires exactly 1 comparison values (you provided 2).") + + fail(t, so("", ShouldNotEndWith, ""), "Expected '' NOT to end with '' (but it did)!") + fail(t, so("superman", ShouldNotEndWith, "man"), "Expected 'superman' NOT to end with 'man' (but it did)!") + pass(t, so("superman", ShouldNotEndWith, "super")) + + fail(t, so(1, ShouldNotEndWith, 2), "Both arguments to this assertion must be strings (you provided int and int).") +} + +func TestShouldContainSubstring(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so("asdf", ShouldContainSubstring), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so("asdf", ShouldContainSubstring, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(123, ShouldContainSubstring, 23), "Both arguments to this assertion must be strings (you provided int and int).") + + pass(t, so("asdf", ShouldContainSubstring, "sd")) + fail(t, so("qwer", ShouldContainSubstring, "sd"), "sd|qwer|Expected 'qwer' to contain substring 'sd' (but it didn't)!") +} + +func TestShouldNotContainSubstring(t *testing.T) { + fail(t, so("asdf", ShouldNotContainSubstring), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so("asdf", ShouldNotContainSubstring, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(123, ShouldNotContainSubstring, 23), "Both arguments to this assertion must be strings (you provided int and int).") + + pass(t, so("qwer", ShouldNotContainSubstring, "sd")) + fail(t, so("asdf", ShouldNotContainSubstring, "sd"), "Expected 'asdf' NOT to contain substring 'sd' (but it did)!") +} + +func TestShouldBeBlank(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so("", ShouldBeBlank, "adsf"), "This assertion requires exactly 0 comparison values (you provided 1).") + fail(t, so(1, ShouldBeBlank), "The argument to this assertion must be a string (you provided int).") + + fail(t, so("asdf", ShouldBeBlank), "|asdf|Expected 'asdf' to be blank (but it wasn't)!") + pass(t, so("", ShouldBeBlank)) +} + +func TestShouldNotBeBlank(t *testing.T) { + fail(t, so("", ShouldNotBeBlank, "adsf"), "This assertion requires exactly 0 comparison values (you provided 1).") + fail(t, so(1, ShouldNotBeBlank), "The argument to this assertion must be a string (you provided int).") + + fail(t, so("", ShouldNotBeBlank), "Expected value to NOT be blank (but it was)!") + pass(t, so("asdf", ShouldNotBeBlank)) +} diff --git a/vendor/github.com/smartystreets/assertions/time.go b/vendor/github.com/smartystreets/assertions/time.go new file mode 100644 index 0000000..7e05026 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/time.go @@ -0,0 +1,202 @@ +package assertions + +import ( + "fmt" + "time" +) + +// ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the first happens before the second. +func ShouldHappenBefore(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + expectedTime, secondOk := expected[0].(time.Time) + + if !firstOk || !secondOk { + return shouldUseTimes + } + + if !actualTime.Before(expectedTime) { + return fmt.Sprintf(shouldHaveHappenedBefore, actualTime, expectedTime, actualTime.Sub(expectedTime)) + } + + return success +} + +// ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that the first happens on or before the second. +func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + expectedTime, secondOk := expected[0].(time.Time) + + if !firstOk || !secondOk { + return shouldUseTimes + } + + if actualTime.Equal(expectedTime) { + return success + } + return ShouldHappenBefore(actualTime, expectedTime) +} + +// ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the first happens after the second. +func ShouldHappenAfter(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + expectedTime, secondOk := expected[0].(time.Time) + + if !firstOk || !secondOk { + return shouldUseTimes + } + if !actualTime.After(expectedTime) { + return fmt.Sprintf(shouldHaveHappenedAfter, actualTime, expectedTime, expectedTime.Sub(actualTime)) + } + return success +} + +// ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that the first happens on or after the second. +func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + expectedTime, secondOk := expected[0].(time.Time) + + if !firstOk || !secondOk { + return shouldUseTimes + } + if actualTime.Equal(expectedTime) { + return success + } + return ShouldHappenAfter(actualTime, expectedTime) +} + +// ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the first happens between (not on) the second and third. +func ShouldHappenBetween(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + min, secondOk := expected[0].(time.Time) + max, thirdOk := expected[1].(time.Time) + + if !firstOk || !secondOk || !thirdOk { + return shouldUseTimes + } + + if !actualTime.After(min) { + return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, min.Sub(actualTime)) + } + if !actualTime.Before(max) { + return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, actualTime.Sub(max)) + } + return success +} + +// ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first happens between or on the second and third. +func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + min, secondOk := expected[0].(time.Time) + max, thirdOk := expected[1].(time.Time) + + if !firstOk || !secondOk || !thirdOk { + return shouldUseTimes + } + if actualTime.Equal(min) || actualTime.Equal(max) { + return success + } + return ShouldHappenBetween(actualTime, min, max) +} + +// ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first +// does NOT happen between or on the second or third. +func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + min, secondOk := expected[0].(time.Time) + max, thirdOk := expected[1].(time.Time) + + if !firstOk || !secondOk || !thirdOk { + return shouldUseTimes + } + if actualTime.Equal(min) || actualTime.Equal(max) { + return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max) + } + if actualTime.After(min) && actualTime.Before(max) { + return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max) + } + return success +} + +// ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments) +// and asserts that the first time.Time happens within or on the duration specified relative to +// the other time.Time. +func ShouldHappenWithin(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + tolerance, secondOk := expected[0].(time.Duration) + threshold, thirdOk := expected[1].(time.Time) + + if !firstOk || !secondOk || !thirdOk { + return shouldUseDurationAndTime + } + + min := threshold.Add(-tolerance) + max := threshold.Add(tolerance) + return ShouldHappenOnOrBetween(actualTime, min, max) +} + +// ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments) +// and asserts that the first time.Time does NOT happen within or on the duration specified relative to +// the other time.Time. +func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string { + if fail := need(2, expected); fail != success { + return fail + } + actualTime, firstOk := actual.(time.Time) + tolerance, secondOk := expected[0].(time.Duration) + threshold, thirdOk := expected[1].(time.Time) + + if !firstOk || !secondOk || !thirdOk { + return shouldUseDurationAndTime + } + + min := threshold.Add(-tolerance) + max := threshold.Add(tolerance) + return ShouldNotHappenOnOrBetween(actualTime, min, max) +} + +// ShouldBeChronological receives a []time.Time slice and asserts that the are +// in chronological order starting with the first time.Time as the earliest. +func ShouldBeChronological(actual interface{}, expected ...interface{}) string { + if fail := need(0, expected); fail != success { + return fail + } + + times, ok := actual.([]time.Time) + if !ok { + return shouldUseTimeSlice + } + + var previous time.Time + for i, current := range times { + if i > 0 && current.Before(previous) { + return fmt.Sprintf(shouldHaveBeenChronological, + i, i-1, previous.String(), i, current.String()) + } + previous = current + } + return "" +} diff --git a/vendor/github.com/smartystreets/assertions/time_test.go b/vendor/github.com/smartystreets/assertions/time_test.go new file mode 100644 index 0000000..f9dda8f --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/time_test.go @@ -0,0 +1,159 @@ +package assertions + +import ( + "fmt" + "testing" + "time" +) + +func TestShouldHappenBefore(t *testing.T) { + fail(t, so(0, ShouldHappenBefore), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenBefore, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenBefore, 1), shouldUseTimes) + fail(t, so(0, ShouldHappenBefore, time.Now()), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenBefore, 0), shouldUseTimes) + + fail(t, so(january3, ShouldHappenBefore, january1), fmt.Sprintf("Expected '%s' to happen before '%s' (it happened '48h0m0s' after)!", pretty(january3), pretty(january1))) + fail(t, so(january3, ShouldHappenBefore, january3), fmt.Sprintf("Expected '%s' to happen before '%s' (it happened '0' after)!", pretty(january3), pretty(january3))) + pass(t, so(january1, ShouldHappenBefore, january3)) +} + +func TestShouldHappenOnOrBefore(t *testing.T) { + fail(t, so(0, ShouldHappenOnOrBefore), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenOnOrBefore, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenOnOrBefore, 1), shouldUseTimes) + fail(t, so(0, ShouldHappenOnOrBefore, time.Now()), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenOnOrBefore, 0), shouldUseTimes) + + fail(t, so(january3, ShouldHappenOnOrBefore, january1), fmt.Sprintf("Expected '%s' to happen before '%s' (it happened '48h0m0s' after)!", pretty(january3), pretty(january1))) + pass(t, so(january3, ShouldHappenOnOrBefore, january3)) + pass(t, so(january1, ShouldHappenOnOrBefore, january3)) +} + +func TestShouldHappenAfter(t *testing.T) { + fail(t, so(0, ShouldHappenAfter), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenAfter, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenAfter, 1), shouldUseTimes) + fail(t, so(0, ShouldHappenAfter, time.Now()), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenAfter, 0), shouldUseTimes) + + fail(t, so(january1, ShouldHappenAfter, january2), fmt.Sprintf("Expected '%s' to happen after '%s' (it happened '24h0m0s' before)!", pretty(january1), pretty(january2))) + fail(t, so(january1, ShouldHappenAfter, january1), fmt.Sprintf("Expected '%s' to happen after '%s' (it happened '0' before)!", pretty(january1), pretty(january1))) + pass(t, so(january3, ShouldHappenAfter, january1)) +} + +func TestShouldHappenOnOrAfter(t *testing.T) { + fail(t, so(0, ShouldHappenOnOrAfter), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenOnOrAfter, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenOnOrAfter, 1), shouldUseTimes) + fail(t, so(0, ShouldHappenOnOrAfter, time.Now()), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenOnOrAfter, 0), shouldUseTimes) + + fail(t, so(january1, ShouldHappenOnOrAfter, january2), fmt.Sprintf("Expected '%s' to happen after '%s' (it happened '24h0m0s' before)!", pretty(january1), pretty(january2))) + pass(t, so(january1, ShouldHappenOnOrAfter, january1)) + pass(t, so(january3, ShouldHappenOnOrAfter, january1)) +} + +func TestShouldHappenBetween(t *testing.T) { + fail(t, so(0, ShouldHappenBetween), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenBetween, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenBetween, 1, 2), shouldUseTimes) + fail(t, so(0, ShouldHappenBetween, time.Now(), time.Now()), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenBetween, 0, time.Now()), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenBetween, time.Now(), 9), shouldUseTimes) + + fail(t, so(january1, ShouldHappenBetween, january2, january4), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '24h0m0s' outside threshold)!", pretty(january1), pretty(january2), pretty(january4))) + fail(t, so(january2, ShouldHappenBetween, january2, january4), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '0' outside threshold)!", pretty(january2), pretty(january2), pretty(january4))) + pass(t, so(january3, ShouldHappenBetween, january2, january4)) + fail(t, so(january4, ShouldHappenBetween, january2, january4), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '0' outside threshold)!", pretty(january4), pretty(january2), pretty(january4))) + fail(t, so(january5, ShouldHappenBetween, january2, january4), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '24h0m0s' outside threshold)!", pretty(january5), pretty(january2), pretty(january4))) +} + +func TestShouldHappenOnOrBetween(t *testing.T) { + fail(t, so(0, ShouldHappenOnOrBetween), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenOnOrBetween, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenOnOrBetween, 1, time.Now()), shouldUseTimes) + fail(t, so(0, ShouldHappenOnOrBetween, time.Now(), 1), shouldUseTimes) + fail(t, so(time.Now(), ShouldHappenOnOrBetween, 0, 1), shouldUseTimes) + + fail(t, so(january1, ShouldHappenOnOrBetween, january2, january4), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '24h0m0s' outside threshold)!", pretty(january1), pretty(january2), pretty(january4))) + pass(t, so(january2, ShouldHappenOnOrBetween, january2, january4)) + pass(t, so(january3, ShouldHappenOnOrBetween, january2, january4)) + pass(t, so(january4, ShouldHappenOnOrBetween, january2, january4)) + fail(t, so(january5, ShouldHappenOnOrBetween, january2, january4), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '24h0m0s' outside threshold)!", pretty(january5), pretty(january2), pretty(january4))) +} + +func TestShouldNotHappenOnOrBetween(t *testing.T) { + fail(t, so(0, ShouldNotHappenOnOrBetween), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(0, ShouldNotHappenOnOrBetween, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(0, ShouldNotHappenOnOrBetween, 1, time.Now()), shouldUseTimes) + fail(t, so(0, ShouldNotHappenOnOrBetween, time.Now(), 1), shouldUseTimes) + fail(t, so(time.Now(), ShouldNotHappenOnOrBetween, 0, 1), shouldUseTimes) + + pass(t, so(january1, ShouldNotHappenOnOrBetween, january2, january4)) + fail(t, so(january2, ShouldNotHappenOnOrBetween, january2, january4), fmt.Sprintf("Expected '%s' to NOT happen on or between '%s' and '%s' (but it did)!", pretty(january2), pretty(january2), pretty(january4))) + fail(t, so(january3, ShouldNotHappenOnOrBetween, january2, january4), fmt.Sprintf("Expected '%s' to NOT happen on or between '%s' and '%s' (but it did)!", pretty(january3), pretty(january2), pretty(january4))) + fail(t, so(january4, ShouldNotHappenOnOrBetween, january2, january4), fmt.Sprintf("Expected '%s' to NOT happen on or between '%s' and '%s' (but it did)!", pretty(january4), pretty(january2), pretty(january4))) + pass(t, so(january5, ShouldNotHappenOnOrBetween, january2, january4)) +} + +func TestShouldHappenWithin(t *testing.T) { + fail(t, so(0, ShouldHappenWithin), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(0, ShouldHappenWithin, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(0, ShouldHappenWithin, 1, 2), shouldUseDurationAndTime) + fail(t, so(0, ShouldHappenWithin, oneDay, time.Now()), shouldUseDurationAndTime) + fail(t, so(time.Now(), ShouldHappenWithin, 0, time.Now()), shouldUseDurationAndTime) + + fail(t, so(january1, ShouldHappenWithin, oneDay, january3), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '24h0m0s' outside threshold)!", pretty(january1), pretty(january2), pretty(january4))) + pass(t, so(january2, ShouldHappenWithin, oneDay, january3)) + pass(t, so(january3, ShouldHappenWithin, oneDay, january3)) + pass(t, so(january4, ShouldHappenWithin, oneDay, january3)) + fail(t, so(january5, ShouldHappenWithin, oneDay, january3), fmt.Sprintf("Expected '%s' to happen between '%s' and '%s' (it happened '24h0m0s' outside threshold)!", pretty(january5), pretty(january2), pretty(january4))) +} + +func TestShouldNotHappenWithin(t *testing.T) { + fail(t, so(0, ShouldNotHappenWithin), "This assertion requires exactly 2 comparison values (you provided 0).") + fail(t, so(0, ShouldNotHappenWithin, 1, 2, 3), "This assertion requires exactly 2 comparison values (you provided 3).") + + fail(t, so(0, ShouldNotHappenWithin, 1, 2), shouldUseDurationAndTime) + fail(t, so(0, ShouldNotHappenWithin, oneDay, time.Now()), shouldUseDurationAndTime) + fail(t, so(time.Now(), ShouldNotHappenWithin, 0, time.Now()), shouldUseDurationAndTime) + + pass(t, so(january1, ShouldNotHappenWithin, oneDay, january3)) + fail(t, so(january2, ShouldNotHappenWithin, oneDay, january3), fmt.Sprintf("Expected '%s' to NOT happen on or between '%s' and '%s' (but it did)!", pretty(january2), pretty(january2), pretty(january4))) + fail(t, so(january3, ShouldNotHappenWithin, oneDay, january3), fmt.Sprintf("Expected '%s' to NOT happen on or between '%s' and '%s' (but it did)!", pretty(january3), pretty(january2), pretty(january4))) + fail(t, so(january4, ShouldNotHappenWithin, oneDay, january3), fmt.Sprintf("Expected '%s' to NOT happen on or between '%s' and '%s' (but it did)!", pretty(january4), pretty(january2), pretty(january4))) + pass(t, so(january5, ShouldNotHappenWithin, oneDay, january3)) +} + +func TestShouldBeChronological(t *testing.T) { + fail(t, so(0, ShouldBeChronological, 1, 2, 3), "This assertion requires exactly 0 comparison values (you provided 3).") + fail(t, so(0, ShouldBeChronological), shouldUseTimeSlice) + fail(t, so([]time.Time{january5, january1}, ShouldBeChronological), + "The 'Time' at index [1] should have happened after the previous one (but it didn't!):\n [0]: 2013-01-05 00:00:00 +0000 UTC\n [1]: 2013-01-01 00:00:00 +0000 UTC (see, it happened before!)") + + pass(t, so([]time.Time{january1, january2, january3, january4, january5}, ShouldBeChronological)) +} + +const layout = "2006-01-02 15:04" + +var january1, _ = time.Parse(layout, "2013-01-01 00:00") +var january2, _ = time.Parse(layout, "2013-01-02 00:00") +var january3, _ = time.Parse(layout, "2013-01-03 00:00") +var january4, _ = time.Parse(layout, "2013-01-04 00:00") +var january5, _ = time.Parse(layout, "2013-01-05 00:00") + +var oneDay, _ = time.ParseDuration("24h0m0s") +var twoDays, _ = time.ParseDuration("48h0m0s") + +func pretty(t time.Time) string { + return fmt.Sprintf("%v", t) +} diff --git a/vendor/github.com/smartystreets/assertions/type.go b/vendor/github.com/smartystreets/assertions/type.go new file mode 100644 index 0000000..3fc00f6 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/type.go @@ -0,0 +1,112 @@ +package assertions + +import ( + "fmt" + "reflect" +) + +// ShouldHaveSameTypeAs receives exactly two parameters and compares their underlying types for equality. +func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + first := reflect.TypeOf(actual) + second := reflect.TypeOf(expected[0]) + + if equal := ShouldEqual(first, second); equal != success { + return serializer.serialize(second, first, fmt.Sprintf(shouldHaveBeenA, actual, second, first)) + } + return success +} + +// ShouldNotHaveSameTypeAs receives exactly two parameters and compares their underlying types for inequality. +func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string { + if fail := need(1, expected); fail != success { + return fail + } + + first := reflect.TypeOf(actual) + second := reflect.TypeOf(expected[0]) + + if equal := ShouldEqual(first, second); equal == success { + return fmt.Sprintf(shouldNotHaveBeenA, actual, second) + } + return success +} + +// ShouldImplement receives exactly two parameters and ensures +// that the first implements the interface type of the second. +func ShouldImplement(actual interface{}, expectedList ...interface{}) string { + if fail := need(1, expectedList); fail != success { + return fail + } + + expected := expectedList[0] + if fail := ShouldBeNil(expected); fail != success { + return shouldCompareWithInterfacePointer + } + + if fail := ShouldNotBeNil(actual); fail != success { + return shouldNotBeNilActual + } + + var actualType reflect.Type + if reflect.TypeOf(actual).Kind() != reflect.Ptr { + actualType = reflect.PtrTo(reflect.TypeOf(actual)) + } else { + actualType = reflect.TypeOf(actual) + } + + expectedType := reflect.TypeOf(expected) + if fail := ShouldNotBeNil(expectedType); fail != success { + return shouldCompareWithInterfacePointer + } + + expectedInterface := expectedType.Elem() + + if actualType == nil { + return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actual) + } + + if !actualType.Implements(expectedInterface) { + return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actualType) + } + return success +} + +// ShouldNotImplement receives exactly two parameters and ensures +// that the first does NOT implement the interface type of the second. +func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string { + if fail := need(1, expectedList); fail != success { + return fail + } + + expected := expectedList[0] + if fail := ShouldBeNil(expected); fail != success { + return shouldCompareWithInterfacePointer + } + + if fail := ShouldNotBeNil(actual); fail != success { + return shouldNotBeNilActual + } + + var actualType reflect.Type + if reflect.TypeOf(actual).Kind() != reflect.Ptr { + actualType = reflect.PtrTo(reflect.TypeOf(actual)) + } else { + actualType = reflect.TypeOf(actual) + } + + expectedType := reflect.TypeOf(expected) + if fail := ShouldNotBeNil(expectedType); fail != success { + return shouldCompareWithInterfacePointer + } + + expectedInterface := expectedType.Elem() + + if actualType.Implements(expectedInterface) { + return fmt.Sprintf(shouldNotHaveImplemented, actualType, expectedInterface) + } + return success +} diff --git a/vendor/github.com/smartystreets/assertions/type_test.go b/vendor/github.com/smartystreets/assertions/type_test.go new file mode 100644 index 0000000..4b8d198 --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/type_test.go @@ -0,0 +1,76 @@ +package assertions + +import ( + "bytes" + "io" + "net/http" + "testing" +) + +func TestShouldHaveSameTypeAs(t *testing.T) { + serializer = newFakeSerializer() + + fail(t, so(1, ShouldHaveSameTypeAs), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldHaveSameTypeAs, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(nil, ShouldHaveSameTypeAs, 0), "int||Expected '' to be: 'int' (but was: '')!") + fail(t, so(1, ShouldHaveSameTypeAs, "asdf"), "string|int|Expected '1' to be: 'string' (but was: 'int')!") + + pass(t, so(1, ShouldHaveSameTypeAs, 0)) + pass(t, so(nil, ShouldHaveSameTypeAs, nil)) +} + +func TestShouldNotHaveSameTypeAs(t *testing.T) { + fail(t, so(1, ShouldNotHaveSameTypeAs), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(1, ShouldNotHaveSameTypeAs, 1, 2, 3), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(1, ShouldNotHaveSameTypeAs, 0), "Expected '1' to NOT be: 'int' (but it was)!") + fail(t, so(nil, ShouldNotHaveSameTypeAs, nil), "Expected '' to NOT be: '' (but it was)!") + + pass(t, so(nil, ShouldNotHaveSameTypeAs, 0)) + pass(t, so(1, ShouldNotHaveSameTypeAs, "asdf")) +} + +func TestShouldImplement(t *testing.T) { + var ioReader *io.Reader = nil + var response http.Response = http.Response{} + var responsePtr *http.Response = new(http.Response) + var reader = bytes.NewBufferString("") + + fail(t, so(reader, ShouldImplement), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(reader, ShouldImplement, ioReader, ioReader), "This assertion requires exactly 1 comparison values (you provided 2).") + fail(t, so(reader, ShouldImplement, ioReader, ioReader, ioReader), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(reader, ShouldImplement, "foo"), shouldCompareWithInterfacePointer) + fail(t, so(reader, ShouldImplement, 1), shouldCompareWithInterfacePointer) + fail(t, so(reader, ShouldImplement, nil), shouldCompareWithInterfacePointer) + + fail(t, so(nil, ShouldImplement, ioReader), shouldNotBeNilActual) + fail(t, so(1, ShouldImplement, ioReader), "Expected: 'io.Reader interface support'\nActual: '*int' does not implement the interface!") + + fail(t, so(response, ShouldImplement, ioReader), "Expected: 'io.Reader interface support'\nActual: '*http.Response' does not implement the interface!") + fail(t, so(responsePtr, ShouldImplement, ioReader), "Expected: 'io.Reader interface support'\nActual: '*http.Response' does not implement the interface!") + pass(t, so(reader, ShouldImplement, ioReader)) + pass(t, so(reader, ShouldImplement, (*io.Reader)(nil))) +} + +func TestShouldNotImplement(t *testing.T) { + var ioReader *io.Reader = nil + var response http.Response = http.Response{} + var responsePtr *http.Response = new(http.Response) + var reader io.Reader = bytes.NewBufferString("") + + fail(t, so(reader, ShouldNotImplement), "This assertion requires exactly 1 comparison values (you provided 0).") + fail(t, so(reader, ShouldNotImplement, ioReader, ioReader), "This assertion requires exactly 1 comparison values (you provided 2).") + fail(t, so(reader, ShouldNotImplement, ioReader, ioReader, ioReader), "This assertion requires exactly 1 comparison values (you provided 3).") + + fail(t, so(reader, ShouldNotImplement, "foo"), shouldCompareWithInterfacePointer) + fail(t, so(reader, ShouldNotImplement, 1), shouldCompareWithInterfacePointer) + fail(t, so(reader, ShouldNotImplement, nil), shouldCompareWithInterfacePointer) + + fail(t, so(reader, ShouldNotImplement, ioReader), "Expected '*bytes.Buffer'\nto NOT implement 'io.Reader' (but it did)!") + fail(t, so(nil, ShouldNotImplement, ioReader), shouldNotBeNilActual) + pass(t, so(1, ShouldNotImplement, ioReader)) + pass(t, so(response, ShouldNotImplement, ioReader)) + pass(t, so(responsePtr, ShouldNotImplement, ioReader)) +} diff --git a/vendor/github.com/smartystreets/assertions/utilities_for_test.go b/vendor/github.com/smartystreets/assertions/utilities_for_test.go new file mode 100644 index 0000000..7243ebc --- /dev/null +++ b/vendor/github.com/smartystreets/assertions/utilities_for_test.go @@ -0,0 +1,75 @@ +package assertions + +import ( + "fmt" + "path" + "runtime" + "strings" + "testing" +) + +func pass(t *testing.T, result string) { + if result != success { + _, file, line, _ := runtime.Caller(1) + base := path.Base(file) + t.Errorf("Expectation should have passed but failed (see %s: line %d): '%s'", base, line, result) + } +} + +func fail(t *testing.T, actual string, expected string) { + actual = format(actual) + expected = format(expected) + + if actual != expected { + if actual == "" { + actual = "(empty)" + } + _, file, line, _ := runtime.Caller(1) + base := path.Base(file) + t.Errorf("Expectation should have failed but passed (see %s: line %d). \nExpected: %s\nActual: %s\n", + base, line, expected, actual) + } +} +func format(message string) string { + message = strings.Replace(message, "\n", " ", -1) + for strings.Contains(message, " ") { + message = strings.Replace(message, " ", " ", -1) + } + return message +} + +type Thing1 struct { + a string +} +type Thing2 struct { + a string +} + +type Thinger interface { + Hi() +} + +type Thing struct{} + +func (self *Thing) Hi() {} + +type IntAlias int +type StringAlias string +type StringSliceAlias []string +type StringStringMapAlias map[string]string + +/******** FakeSerialzier ********/ + +type fakeSerializer struct{} + +func (self *fakeSerializer) serialize(expected, actual interface{}, message string) string { + return fmt.Sprintf("%v|%v|%s", expected, actual, message) +} + +func (self *fakeSerializer) serializeDetailed(expected, actual interface{}, message string) string { + return fmt.Sprintf("%v|%v|%s", expected, actual, message) +} + +func newFakeSerializer() *fakeSerializer { + return new(fakeSerializer) +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/assertions.go b/vendor/github.com/smartystreets/goconvey/convey/assertions.go new file mode 100644 index 0000000..ce146c3 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/assertions.go @@ -0,0 +1,67 @@ +package convey + +import "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions" + +var ( + ShouldEqual = assertions.ShouldEqual + ShouldNotEqual = assertions.ShouldNotEqual + ShouldAlmostEqual = assertions.ShouldAlmostEqual + ShouldNotAlmostEqual = assertions.ShouldNotAlmostEqual + ShouldResemble = assertions.ShouldResemble + ShouldNotResemble = assertions.ShouldNotResemble + ShouldPointTo = assertions.ShouldPointTo + ShouldNotPointTo = assertions.ShouldNotPointTo + ShouldBeNil = assertions.ShouldBeNil + ShouldNotBeNil = assertions.ShouldNotBeNil + ShouldBeTrue = assertions.ShouldBeTrue + ShouldBeFalse = assertions.ShouldBeFalse + ShouldBeZeroValue = assertions.ShouldBeZeroValue + + ShouldBeGreaterThan = assertions.ShouldBeGreaterThan + ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo + ShouldBeLessThan = assertions.ShouldBeLessThan + ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo + ShouldBeBetween = assertions.ShouldBeBetween + ShouldNotBeBetween = assertions.ShouldNotBeBetween + ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual + ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual + + ShouldContain = assertions.ShouldContain + ShouldNotContain = assertions.ShouldNotContain + ShouldContainKey = assertions.ShouldContainKey + ShouldNotContainKey = assertions.ShouldNotContainKey + ShouldBeIn = assertions.ShouldBeIn + ShouldNotBeIn = assertions.ShouldNotBeIn + ShouldBeEmpty = assertions.ShouldBeEmpty + ShouldNotBeEmpty = assertions.ShouldNotBeEmpty + + ShouldStartWith = assertions.ShouldStartWith + ShouldNotStartWith = assertions.ShouldNotStartWith + ShouldEndWith = assertions.ShouldEndWith + ShouldNotEndWith = assertions.ShouldNotEndWith + ShouldBeBlank = assertions.ShouldBeBlank + ShouldNotBeBlank = assertions.ShouldNotBeBlank + ShouldContainSubstring = assertions.ShouldContainSubstring + ShouldNotContainSubstring = assertions.ShouldNotContainSubstring + + ShouldPanic = assertions.ShouldPanic + ShouldNotPanic = assertions.ShouldNotPanic + ShouldPanicWith = assertions.ShouldPanicWith + ShouldNotPanicWith = assertions.ShouldNotPanicWith + + ShouldHaveSameTypeAs = assertions.ShouldHaveSameTypeAs + ShouldNotHaveSameTypeAs = assertions.ShouldNotHaveSameTypeAs + ShouldImplement = assertions.ShouldImplement + ShouldNotImplement = assertions.ShouldNotImplement + + ShouldHappenBefore = assertions.ShouldHappenBefore + ShouldHappenOnOrBefore = assertions.ShouldHappenOnOrBefore + ShouldHappenAfter = assertions.ShouldHappenAfter + ShouldHappenOnOrAfter = assertions.ShouldHappenOnOrAfter + ShouldHappenBetween = assertions.ShouldHappenBetween + ShouldHappenOnOrBetween = assertions.ShouldHappenOnOrBetween + ShouldNotHappenOnOrBetween = assertions.ShouldNotHappenOnOrBetween + ShouldHappenWithin = assertions.ShouldHappenWithin + ShouldNotHappenWithin = assertions.ShouldNotHappenWithin + ShouldBeChronological = assertions.ShouldBeChronological +) diff --git a/vendor/github.com/smartystreets/goconvey/convey/context.go b/vendor/github.com/smartystreets/goconvey/convey/context.go new file mode 100644 index 0000000..863bdaa --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/context.go @@ -0,0 +1,272 @@ +package convey + +import ( + "fmt" + + "github.com/moul/ssh2docker/vendor/github.com/jtolds/gls" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" +) + +type conveyErr struct { + fmt string + params []interface{} +} + +func (e *conveyErr) Error() string { + return fmt.Sprintf(e.fmt, e.params...) +} + +func conveyPanic(fmt string, params ...interface{}) { + panic(&conveyErr{fmt, params}) +} + +const ( + missingGoTest = `Top-level calls to Convey(...) need a reference to the *testing.T. + Hint: Convey("description here", t, func() { /* notice that the second argument was the *testing.T (t)! */ }) ` + extraGoTest = `Only the top-level call to Convey(...) needs a reference to the *testing.T.` + noStackContext = "Convey operation made without context on goroutine stack.\n" + + "Hint: Perhaps you meant to use `Convey(..., func(c C){...})` ?" + differentConveySituations = "Different set of Convey statements on subsequent pass!\nDid not expect %#v." + multipleIdenticalConvey = "Multiple convey suites with identical names: %#v" +) + +const ( + failureHalt = "___FAILURE_HALT___" + + nodeKey = "node" +) + +///////////////////////////////// Stack Context ///////////////////////////////// + +func getCurrentContext() *context { + ctx, ok := ctxMgr.GetValue(nodeKey) + if ok { + return ctx.(*context) + } + return nil +} + +func mustGetCurrentContext() *context { + ctx := getCurrentContext() + if ctx == nil { + conveyPanic(noStackContext) + } + return ctx +} + +//////////////////////////////////// Context //////////////////////////////////// + +// context magically handles all coordination of Convey's and So assertions. +// +// It is tracked on the stack as goroutine-local-storage with the gls package, +// or explicitly if the user decides to call convey like: +// +// Convey(..., func(c C) { +// c.So(...) +// }) +// +// This implements the `C` interface. +type context struct { + reporter reporting.Reporter + + children map[string]*context + + resets []func() + + executedOnce bool + expectChildRun *bool + complete bool + + focus bool + failureMode FailureMode +} + +// rootConvey is the main entry point to a test suite. This is called when +// there's no context in the stack already, and items must contain a `t` object, +// or this panics. +func rootConvey(items ...interface{}) { + entry := discover(items) + + if entry.Test == nil { + conveyPanic(missingGoTest) + } + + expectChildRun := true + ctx := &context{ + reporter: buildReporter(), + + children: make(map[string]*context), + + expectChildRun: &expectChildRun, + + focus: entry.Focus, + failureMode: defaultFailureMode.combine(entry.FailMode), + } + ctxMgr.SetValues(gls.Values{nodeKey: ctx}, func() { + ctx.reporter.BeginStory(reporting.NewStoryReport(entry.Test)) + defer ctx.reporter.EndStory() + + for ctx.shouldVisit() { + ctx.conveyInner(entry.Situation, entry.Func) + expectChildRun = true + } + }) +} + +//////////////////////////////////// Methods //////////////////////////////////// + +func (ctx *context) SkipConvey(items ...interface{}) { + ctx.Convey(items, skipConvey) +} + +func (ctx *context) FocusConvey(items ...interface{}) { + ctx.Convey(items, focusConvey) +} + +func (ctx *context) Convey(items ...interface{}) { + entry := discover(items) + + // we're a branch, or leaf (on the wind) + if entry.Test != nil { + conveyPanic(extraGoTest) + } + if ctx.focus && !entry.Focus { + return + } + + var inner_ctx *context + if ctx.executedOnce { + var ok bool + inner_ctx, ok = ctx.children[entry.Situation] + if !ok { + conveyPanic(differentConveySituations, entry.Situation) + } + } else { + if _, ok := ctx.children[entry.Situation]; ok { + conveyPanic(multipleIdenticalConvey, entry.Situation) + } + inner_ctx = &context{ + reporter: ctx.reporter, + + children: make(map[string]*context), + + expectChildRun: ctx.expectChildRun, + + focus: entry.Focus, + failureMode: ctx.failureMode.combine(entry.FailMode), + } + ctx.children[entry.Situation] = inner_ctx + } + + if inner_ctx.shouldVisit() { + ctxMgr.SetValues(gls.Values{nodeKey: inner_ctx}, func() { + inner_ctx.conveyInner(entry.Situation, entry.Func) + }) + } +} + +func (ctx *context) SkipSo(stuff ...interface{}) { + ctx.assertionReport(reporting.NewSkipReport()) +} + +func (ctx *context) So(actual interface{}, assert assertion, expected ...interface{}) { + if result := assert(actual, expected...); result == assertionSuccess { + ctx.assertionReport(reporting.NewSuccessReport()) + } else { + ctx.assertionReport(reporting.NewFailureReport(result)) + } +} + +func (ctx *context) Reset(action func()) { + /* TODO: Failure mode configuration */ + ctx.resets = append(ctx.resets, action) +} + +func (ctx *context) Print(items ...interface{}) (int, error) { + fmt.Fprint(ctx.reporter, items...) + return fmt.Print(items...) +} + +func (ctx *context) Println(items ...interface{}) (int, error) { + fmt.Fprintln(ctx.reporter, items...) + return fmt.Println(items...) +} + +func (ctx *context) Printf(format string, items ...interface{}) (int, error) { + fmt.Fprintf(ctx.reporter, format, items...) + return fmt.Printf(format, items...) +} + +//////////////////////////////////// Private //////////////////////////////////// + +// shouldVisit returns true iff we should traverse down into a Convey. Note +// that just because we don't traverse a Convey this time, doesn't mean that +// we may not traverse it on a subsequent pass. +func (c *context) shouldVisit() bool { + return !c.complete && *c.expectChildRun +} + +// conveyInner is the function which actually executes the user's anonymous test +// function body. At this point, Convey or RootConvey has decided that this +// function should actually run. +func (ctx *context) conveyInner(situation string, f func(C)) { + // Record/Reset state for next time. + defer func() { + ctx.executedOnce = true + + // This is only needed at the leaves, but there's no harm in also setting it + // when returning from branch Convey's + *ctx.expectChildRun = false + }() + + // Set up+tear down our scope for the reporter + ctx.reporter.Enter(reporting.NewScopeReport(situation)) + defer ctx.reporter.Exit() + + // Recover from any panics in f, and assign the `complete` status for this + // node of the tree. + defer func() { + ctx.complete = true + if problem := recover(); problem != nil { + if problem, ok := problem.(*conveyErr); ok { + panic(problem) + } + if problem != failureHalt { + ctx.reporter.Report(reporting.NewErrorReport(problem)) + } + } else { + for _, child := range ctx.children { + if !child.complete { + ctx.complete = false + return + } + } + } + }() + + // Resets are registered as the `f` function executes, so nil them here. + // All resets are run in registration order (FIFO). + ctx.resets = []func(){} + defer func() { + for _, r := range ctx.resets { + // panics handled by the previous defer + r() + } + }() + + if f == nil { + // if f is nil, this was either a Convey(..., nil), or a SkipConvey + ctx.reporter.Report(reporting.NewSkipReport()) + } else { + f(ctx) + } +} + +// assertionReport is a helper for So and SkipSo which makes the report and +// then possibly panics, depending on the current context's failureMode. +func (ctx *context) assertionReport(r *reporting.AssertionResult) { + ctx.reporter.Report(r) + if r.Failure != "" && ctx.failureMode == FailureHalts { + panic(failureHalt) + } +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/convey.goconvey b/vendor/github.com/smartystreets/goconvey/convey/convey.goconvey new file mode 100644 index 0000000..a2d9327 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/convey.goconvey @@ -0,0 +1,4 @@ +#ignore +-timeout=1s +#-covermode=count +#-coverpkg=github.com/smartystreets/goconvey/convey,github.com/smartystreets/goconvey/convey/gotest,github.com/smartystreets/goconvey/convey/reporting \ No newline at end of file diff --git a/vendor/github.com/smartystreets/goconvey/convey/discovery.go b/vendor/github.com/smartystreets/goconvey/convey/discovery.go new file mode 100644 index 0000000..eb8d4cb --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/discovery.go @@ -0,0 +1,103 @@ +package convey + +type actionSpecifier uint8 + +const ( + noSpecifier actionSpecifier = iota + skipConvey + focusConvey +) + +type suite struct { + Situation string + Test t + Focus bool + Func func(C) // nil means skipped + FailMode FailureMode +} + +func newSuite(situation string, failureMode FailureMode, f func(C), test t, specifier actionSpecifier) *suite { + ret := &suite{ + Situation: situation, + Test: test, + Func: f, + FailMode: failureMode, + } + switch specifier { + case skipConvey: + ret.Func = nil + case focusConvey: + ret.Focus = true + } + return ret +} + +func discover(items []interface{}) *suite { + name, items := parseName(items) + test, items := parseGoTest(items) + failure, items := parseFailureMode(items) + action, items := parseAction(items) + specifier, items := parseSpecifier(items) + + if len(items) != 0 { + conveyPanic(parseError) + } + + return newSuite(name, failure, action, test, specifier) +} +func item(items []interface{}) interface{} { + if len(items) == 0 { + conveyPanic(parseError) + } + return items[0] +} +func parseName(items []interface{}) (string, []interface{}) { + if name, parsed := item(items).(string); parsed { + return name, items[1:] + } + conveyPanic(parseError) + panic("never get here") +} +func parseGoTest(items []interface{}) (t, []interface{}) { + if test, parsed := item(items).(t); parsed { + return test, items[1:] + } + return nil, items +} +func parseFailureMode(items []interface{}) (FailureMode, []interface{}) { + if mode, parsed := item(items).(FailureMode); parsed { + return mode, items[1:] + } + return FailureInherits, items +} +func parseAction(items []interface{}) (func(C), []interface{}) { + switch x := item(items).(type) { + case nil: + return nil, items[1:] + case func(C): + return x, items[1:] + case func(): + return func(C) { x() }, items[1:] + } + conveyPanic(parseError) + panic("never get here") +} +func parseSpecifier(items []interface{}) (actionSpecifier, []interface{}) { + if len(items) == 0 { + return noSpecifier, items + } + if spec, ok := items[0].(actionSpecifier); ok { + return spec, items[1:] + } + conveyPanic(parseError) + panic("never get here") +} + +// This interface allows us to pass the *testing.T struct +// throughout the internals of this package without ever +// having to import the "testing" package. +type t interface { + Fail() +} + +const parseError = "You must provide a name (string), then a *testing.T (if in outermost scope), an optional FailureMode, and then an action (func())." diff --git a/vendor/github.com/smartystreets/goconvey/convey/doc.go b/vendor/github.com/smartystreets/goconvey/convey/doc.go new file mode 100644 index 0000000..ad43cfa --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/doc.go @@ -0,0 +1,218 @@ +// Package convey contains all of the public-facing entry points to this project. +// This means that it should never be required of the user to import any other +// packages from this project as they serve internal purposes. +package convey + +import "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" + +////////////////////////////////// suite ////////////////////////////////// + +// C is the Convey context which you can optionally obtain in your action +// by calling Convey like: +// +// Convey(..., func(c C) { +// ... +// }) +// +// See the documentation on Convey for more details. +// +// All methods in this context behave identically to the global functions of the +// same name in this package. +type C interface { + Convey(items ...interface{}) + SkipConvey(items ...interface{}) + FocusConvey(items ...interface{}) + + So(actual interface{}, assert assertion, expected ...interface{}) + SkipSo(stuff ...interface{}) + + Reset(action func()) + + Println(items ...interface{}) (int, error) + Print(items ...interface{}) (int, error) + Printf(format string, items ...interface{}) (int, error) +} + +// Convey is the method intended for use when declaring the scopes of +// a specification. Each scope has a description and a func() which may contain +// other calls to Convey(), Reset() or Should-style assertions. Convey calls can +// be nested as far as you see fit. +// +// IMPORTANT NOTE: The top-level Convey() within a Test method +// must conform to the following signature: +// +// Convey(description string, t *testing.T, action func()) +// +// All other calls should look like this (no need to pass in *testing.T): +// +// Convey(description string, action func()) +// +// Don't worry, goconvey will panic if you get it wrong so you can fix it. +// +// Additionally, you may explicitly obtain access to the Convey context by doing: +// +// Convey(description string, action func(c C)) +// +// You may need to do this if you want to pass the context through to a +// goroutine, or to close over the context in a handler to a library which +// calls your handler in a goroutine (httptest comes to mind). +// +// All Convey()-blocks also accept an optional parameter of FailureMode which sets +// how goconvey should treat failures for So()-assertions in the block and +// nested blocks. See the constants in this file for the available options. +// +// By default it will inherit from its parent block and the top-level blocks +// default to the FailureHalts setting. +// +// This parameter is inserted before the block itself: +// +// Convey(description string, t *testing.T, mode FailureMode, action func()) +// Convey(description string, mode FailureMode, action func()) +// +// See the examples package for, well, examples. +func Convey(items ...interface{}) { + if ctx := getCurrentContext(); ctx == nil { + rootConvey(items...) + } else { + ctx.Convey(items...) + } +} + +// SkipConvey is analagous to Convey except that the scope is not executed +// (which means that child scopes defined within this scope are not run either). +// The reporter will be notified that this step was skipped. +func SkipConvey(items ...interface{}) { + Convey(append(items, skipConvey)...) +} + +// FocusConvey is has the inverse effect of SkipConvey. If the top-level +// Convey is changed to `FocusConvey`, only nested scopes that are defined +// with FocusConvey will be run. The rest will be ignored completely. This +// is handy when debugging a large suite that runs a misbehaving function +// repeatedly as you can disable all but one of that function +// without swaths of `SkipConvey` calls, just a targeted chain of calls +// to FocusConvey. +func FocusConvey(items ...interface{}) { + Convey(append(items, focusConvey)...) +} + +// Reset registers a cleanup function to be run after each Convey() +// in the same scope. See the examples package for a simple use case. +func Reset(action func()) { + mustGetCurrentContext().Reset(action) +} + +/////////////////////////////////// Assertions /////////////////////////////////// + +// assertion is an alias for a function with a signature that the convey.So() +// method can handle. Any future or custom assertions should conform to this +// method signature. The return value should be an empty string if the assertion +// passes and a well-formed failure message if not. +type assertion func(actual interface{}, expected ...interface{}) string + +const assertionSuccess = "" + +// So is the means by which assertions are made against the system under test. +// The majority of exported names in the assertions package begin with the word +// 'Should' and describe how the first argument (actual) should compare with any +// of the final (expected) arguments. How many final arguments are accepted +// depends on the particular assertion that is passed in as the assert argument. +// See the examples package for use cases and the assertions package for +// documentation on specific assertion methods. A failing assertion will +// cause t.Fail() to be invoked--you should never call this method (or other +// failure-inducing methods) in your test code. Leave that to GoConvey. +func So(actual interface{}, assert assertion, expected ...interface{}) { + mustGetCurrentContext().So(actual, assert, expected...) +} + +// SkipSo is analagous to So except that the assertion that would have been passed +// to So is not executed and the reporter is notified that the assertion was skipped. +func SkipSo(stuff ...interface{}) { + mustGetCurrentContext().SkipSo() +} + +// FailureMode is a type which determines how the So() blocks should fail +// if their assertion fails. See constants further down for acceptable values +type FailureMode string + +const ( + + // FailureContinues is a failure mode which prevents failing + // So()-assertions from halting Convey-block execution, instead + // allowing the test to continue past failing So()-assertions. + FailureContinues FailureMode = "continue" + + // FailureHalts is the default setting for a top-level Convey()-block + // and will cause all failing So()-assertions to halt further execution + // in that test-arm and continue on to the next arm. + FailureHalts FailureMode = "halt" + + // FailureInherits is the default setting for failure-mode, it will + // default to the failure-mode of the parent block. You should never + // need to specify this mode in your tests.. + FailureInherits FailureMode = "inherits" +) + +func (f FailureMode) combine(other FailureMode) FailureMode { + if other == FailureInherits { + return f + } + return other +} + +var defaultFailureMode FailureMode = FailureHalts + +// SetDefaultFailureMode allows you to specify the default failure mode +// for all Convey blocks. It is meant to be used in an init function to +// allow the default mode to be changdd across all tests for an entire packgae +// but it can be used anywhere. +func SetDefaultFailureMode(mode FailureMode) { + if mode == FailureContinues || mode == FailureHalts { + defaultFailureMode = mode + } else { + panic("You may only use the constants named 'FailureContinues' and 'FailureHalts' as default failure modes.") + } +} + +//////////////////////////////////// Print functions //////////////////////////////////// + +// Print is analogous to fmt.Print (and it even calls fmt.Print). It ensures that +// output is aligned with the corresponding scopes in the web UI. +func Print(items ...interface{}) (written int, err error) { + return mustGetCurrentContext().Print(items...) +} + +// Print is analogous to fmt.Println (and it even calls fmt.Println). It ensures that +// output is aligned with the corresponding scopes in the web UI. +func Println(items ...interface{}) (written int, err error) { + return mustGetCurrentContext().Println(items...) +} + +// Print is analogous to fmt.Printf (and it even calls fmt.Printf). It ensures that +// output is aligned with the corresponding scopes in the web UI. +func Printf(format string, items ...interface{}) (written int, err error) { + return mustGetCurrentContext().Printf(format, items...) +} + +/////////////////////////////////////////////////////////////////////////////// + +// SuppressConsoleStatistics prevents automatic printing of console statistics. +// Calling PrintConsoleStatistics explicitly will force printing of statistics. +func SuppressConsoleStatistics() { + reporting.SuppressConsoleStatistics() +} + +// ConsoleStatistics may be called at any time to print assertion statistics. +// Generally, the best place to do this would be in a TestMain function, +// after all tests have been run. Something like this: +// +// func TestMain(m *testing.M) { +// convey.SuppressConsoleStatistics() +// result := m.Run() +// convey.PrintConsoleStatistics() +// os.Exit(result) +// } +// +func PrintConsoleStatistics() { + reporting.PrintConsoleStatistics() +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/focused_execution_test.go b/vendor/github.com/smartystreets/goconvey/convey/focused_execution_test.go new file mode 100644 index 0000000..294e32f --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/focused_execution_test.go @@ -0,0 +1,72 @@ +package convey + +import "testing" + +func TestFocusOnlyAtTopLevel(t *testing.T) { + output := prepare() + + FocusConvey("hi", t, func() { + output += "done" + }) + + expectEqual(t, "done", output) +} + +func TestFocus(t *testing.T) { + output := prepare() + + FocusConvey("hi", t, func() { + output += "1" + + Convey("bye", func() { + output += "2" + }) + }) + + expectEqual(t, "1", output) +} + +func TestNestedFocus(t *testing.T) { + output := prepare() + + FocusConvey("hi", t, func() { + output += "1" + + Convey("This shouldn't run", func() { + output += "boink!" + }) + + FocusConvey("This should run", func() { + output += "2" + + FocusConvey("The should run too", func() { + output += "3" + + }) + + Convey("The should NOT run", func() { + output += "blah blah blah!" + }) + }) + }) + + expectEqual(t, "123", output) +} + +func TestForgotTopLevelFocus(t *testing.T) { + output := prepare() + + Convey("1", t, func() { + output += "1" + + FocusConvey("This will be run because the top-level lacks Focus", func() { + output += "2" + }) + + Convey("3", func() { + output += "3" + }) + }) + + expectEqual(t, "1213", output) +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/gotest/doc_test.go b/vendor/github.com/smartystreets/goconvey/convey/gotest/doc_test.go new file mode 100644 index 0000000..1b6406b --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/gotest/doc_test.go @@ -0,0 +1 @@ +package gotest diff --git a/vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go b/vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go new file mode 100644 index 0000000..2855761 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go @@ -0,0 +1,37 @@ +// Package gotest contains internal functionality. Although this package +// contains one or more exported names it is not intended for public +// consumption. See the examples package for how to use this project. +package gotest + +import ( + "fmt" + "runtime" + "strings" +) + +func FormatExternalFileAndLine() string { + file, line, _ := ResolveExternalCaller() + if line == -1 { + return "" // panic? + } + return fmt.Sprintf("%s:%d", file, line) +} + +func ResolveExternalCaller() (file string, line int, name string) { + var caller_id uintptr + callers := runtime.Callers(0, callStack) + + for x := 0; x < callers; x++ { + caller_id, file, line, _ = runtime.Caller(x) + if strings.HasSuffix(file, "_test.go") || strings.HasSuffix(file, "_tests.go") { + name = runtime.FuncForPC(caller_id).Name() + return + } + } + file, line, name = "", -1, "" + return // panic? +} + +const maxStackDepth = 100 // This had better be enough... + +var callStack []uintptr = make([]uintptr, maxStackDepth, maxStackDepth) diff --git a/vendor/github.com/smartystreets/goconvey/convey/init.go b/vendor/github.com/smartystreets/goconvey/convey/init.go new file mode 100644 index 0000000..a91624e --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/init.go @@ -0,0 +1,76 @@ +package convey + +import ( + "flag" + "os" + + "github.com/moul/ssh2docker/vendor/github.com/jtolds/gls" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/assertions" + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" +) + +func init() { + assertions.GoConveyMode(true) + + declareFlags() + + ctxMgr = gls.NewContextManager() +} + +func declareFlags() { + flag.BoolVar(&json, "json", false, "When true, emits results in JSON blocks. Default: 'false'") + flag.BoolVar(&silent, "silent", false, "When true, all output from GoConvey is suppressed.") + flag.BoolVar(&story, "story", false, "When true, emits story output, otherwise emits dot output. When not provided, this flag mirros the value of the '-test.v' flag") + + if noStoryFlagProvided() { + story = verboseEnabled + } + + // FYI: flag.Parse() is called from the testing package. +} + +func noStoryFlagProvided() bool { + return !story && !storyDisabled +} + +func buildReporter() reporting.Reporter { + switch { + case testReporter != nil: + return testReporter + case json: + return reporting.BuildJsonReporter() + case silent: + return reporting.BuildSilentReporter() + case story: + return reporting.BuildStoryReporter() + default: + return reporting.BuildDotReporter() + } +} + +var ( + ctxMgr *gls.ContextManager + + // only set by internal tests + testReporter reporting.Reporter +) + +var ( + json bool + silent bool + story bool + + verboseEnabled = flagFound("-test.v=true") + storyDisabled = flagFound("-story=false") +) + +// flagFound parses the command line args manually for flags defined in other +// packages. Like the '-v' flag from the "testing" package, for instance. +func flagFound(flagValue string) bool { + for _, arg := range os.Args { + if arg == flagValue { + return true + } + } + return false +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/isolated_execution_test.go b/vendor/github.com/smartystreets/goconvey/convey/isolated_execution_test.go new file mode 100644 index 0000000..7e22b3c --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/isolated_execution_test.go @@ -0,0 +1,774 @@ +package convey + +import ( + "strconv" + "testing" + "time" +) + +func TestSingleScope(t *testing.T) { + output := prepare() + + Convey("hi", t, func() { + output += "done" + }) + + expectEqual(t, "done", output) +} + +func TestSingleScopeWithMultipleConveys(t *testing.T) { + output := prepare() + + Convey("1", t, func() { + output += "1" + }) + + Convey("2", t, func() { + output += "2" + }) + + expectEqual(t, "12", output) +} + +func TestNestedScopes(t *testing.T) { + output := prepare() + + Convey("a", t, func() { + output += "a " + + Convey("bb", func() { + output += "bb " + + Convey("ccc", func() { + output += "ccc | " + }) + }) + }) + + expectEqual(t, "a bb ccc | ", output) +} + +func TestNestedScopesWithIsolatedExecution(t *testing.T) { + output := prepare() + + Convey("a", t, func() { + output += "a " + + Convey("aa", func() { + output += "aa " + + Convey("aaa", func() { + output += "aaa | " + }) + + Convey("aaa1", func() { + output += "aaa1 | " + }) + }) + + Convey("ab", func() { + output += "ab " + + Convey("abb", func() { + output += "abb | " + }) + }) + }) + + expectEqual(t, "a aa aaa | a aa aaa1 | a ab abb | ", output) +} + +func TestSingleScopeWithConveyAndNestedReset(t *testing.T) { + output := prepare() + + Convey("1", t, func() { + output += "1" + + Reset(func() { + output += "a" + }) + }) + + expectEqual(t, "1a", output) +} + +func TestPanicingReset(t *testing.T) { + output := prepare() + + Convey("1", t, func() { + output += "1" + + Reset(func() { + panic("nooo") + }) + + Convey("runs since the reset hasn't yet", func() { + output += "a" + }) + + Convey("but this doesnt", func() { + output += "nope" + }) + }) + + expectEqual(t, "1a", output) +} + +func TestSingleScopeWithMultipleRegistrationsAndReset(t *testing.T) { + output := prepare() + + Convey("reset after each nested convey", t, func() { + Convey("first output", func() { + output += "1" + }) + + Convey("second output", func() { + output += "2" + }) + + Reset(func() { + output += "a" + }) + }) + + expectEqual(t, "1a2a", output) +} + +func TestSingleScopeWithMultipleRegistrationsAndMultipleResets(t *testing.T) { + output := prepare() + + Convey("each reset is run at end of each nested convey", t, func() { + Convey("1", func() { + output += "1" + }) + + Convey("2", func() { + output += "2" + }) + + Reset(func() { + output += "a" + }) + + Reset(func() { + output += "b" + }) + }) + + expectEqual(t, "1ab2ab", output) +} + +func Test_Failure_AtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) { + output := prepare() + + Convey("This step fails", t, func() { + So(1, ShouldEqual, 2) + + Convey("this should NOT be executed", func() { + output += "a" + }) + }) + + expectEqual(t, "", output) +} + +func Test_Panic_AtHigherLevelScopePreventsChildScopesFromRunning(t *testing.T) { + output := prepare() + + Convey("This step panics", t, func() { + Convey("this happens, because the panic didn't happen yet", func() { + output += "1" + }) + + output += "a" + + Convey("this should NOT be executed", func() { + output += "2" + }) + + output += "b" + + panic("Hi") + + output += "nope" + }) + + expectEqual(t, "1ab", output) +} + +func Test_Panic_InChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) { + output := prepare() + + Convey("This is the parent", t, func() { + Convey("This step panics", func() { + panic("Hi") + output += "1" + }) + + Convey("This sibling should execute", func() { + output += "2" + }) + }) + + expectEqual(t, "2", output) +} + +func Test_Failure_InChildScopeDoes_NOT_PreventExecutionOfSiblingScopes(t *testing.T) { + output := prepare() + + Convey("This is the parent", t, func() { + Convey("This step fails", func() { + So(1, ShouldEqual, 2) + output += "1" + }) + + Convey("This sibling should execute", func() { + output += "2" + }) + }) + + expectEqual(t, "2", output) +} + +func TestResetsAreAlwaysExecutedAfterScope_Panics(t *testing.T) { + output := prepare() + + Convey("This is the parent", t, func() { + Convey("This step panics", func() { + panic("Hi") + output += "1" + }) + + Convey("This sibling step does not panic", func() { + output += "a" + + Reset(func() { + output += "b" + }) + }) + + Reset(func() { + output += "2" + }) + }) + + expectEqual(t, "2ab2", output) +} + +func TestResetsAreAlwaysExecutedAfterScope_Failures(t *testing.T) { + output := prepare() + + Convey("This is the parent", t, func() { + Convey("This step fails", func() { + So(1, ShouldEqual, 2) + output += "1" + }) + + Convey("This sibling step does not fail", func() { + output += "a" + + Reset(func() { + output += "b" + }) + }) + + Reset(func() { + output += "2" + }) + }) + + expectEqual(t, "2ab2", output) +} + +func TestSkipTopLevel(t *testing.T) { + output := prepare() + + SkipConvey("hi", t, func() { + output += "This shouldn't be executed!" + }) + + expectEqual(t, "", output) +} + +func TestSkipNestedLevel(t *testing.T) { + output := prepare() + + Convey("hi", t, func() { + output += "yes" + + SkipConvey("bye", func() { + output += "no" + }) + }) + + expectEqual(t, "yes", output) +} + +func TestSkipNestedLevelSkipsAllChildLevels(t *testing.T) { + output := prepare() + + Convey("hi", t, func() { + output += "yes" + + SkipConvey("bye", func() { + output += "no" + + Convey("byebye", func() { + output += "no-no" + }) + }) + }) + + expectEqual(t, "yes", output) +} + +func TestIterativeConveys(t *testing.T) { + output := prepare() + + Convey("Test", t, func() { + for x := 0; x < 10; x++ { + y := strconv.Itoa(x) + + Convey(y, func() { + output += y + }) + } + }) + + expectEqual(t, "0123456789", output) +} + +func TestClosureVariables(t *testing.T) { + output := prepare() + + i := 0 + + Convey("A", t, func() { + i = i + 1 + j := i + + output += "A" + strconv.Itoa(i) + " " + + Convey("B", func() { + k := j + j = j + 1 + + output += "B" + strconv.Itoa(k) + " " + + Convey("C", func() { + output += "C" + strconv.Itoa(k) + strconv.Itoa(j) + " " + }) + + Convey("D", func() { + output += "D" + strconv.Itoa(k) + strconv.Itoa(j) + " " + }) + }) + + Convey("C", func() { + output += "C" + strconv.Itoa(j) + " " + }) + }) + + output += "D" + strconv.Itoa(i) + " " + + expectEqual(t, "A1 B1 C12 A2 B2 D23 A3 C3 D3 ", output) +} + +func TestClosureVariablesWithReset(t *testing.T) { + output := prepare() + + i := 0 + + Convey("A", t, func() { + i = i + 1 + j := i + + output += "A" + strconv.Itoa(i) + " " + + Reset(func() { + output += "R" + strconv.Itoa(i) + strconv.Itoa(j) + " " + }) + + Convey("B", func() { + output += "B" + strconv.Itoa(j) + " " + }) + + Convey("C", func() { + output += "C" + strconv.Itoa(j) + " " + }) + }) + + output += "D" + strconv.Itoa(i) + " " + + expectEqual(t, "A1 B1 R11 A2 C2 R22 D2 ", output) +} + +func TestWrappedSimple(t *testing.T) { + prepare() + output := resetTestString{""} + + Convey("A", t, func() { + func() { + output.output += "A " + + Convey("B", func() { + output.output += "B " + + Convey("C", func() { + output.output += "C " + }) + + }) + + Convey("D", func() { + output.output += "D " + }) + }() + }) + + expectEqual(t, "A B C A D ", output.output) +} + +type resetTestString struct { + output string +} + +func addReset(o *resetTestString, f func()) func() { + return func() { + Reset(func() { + o.output += "R " + }) + + f() + } +} + +func TestWrappedReset(t *testing.T) { + prepare() + output := resetTestString{""} + + Convey("A", t, addReset(&output, func() { + output.output += "A " + + Convey("B", func() { + output.output += "B " + }) + + Convey("C", func() { + output.output += "C " + }) + })) + + expectEqual(t, "A B R A C R ", output.output) +} + +func TestWrappedReset2(t *testing.T) { + prepare() + output := resetTestString{""} + + Convey("A", t, func() { + Reset(func() { + output.output += "R " + }) + + func() { + output.output += "A " + + Convey("B", func() { + output.output += "B " + + Convey("C", func() { + output.output += "C " + }) + }) + + Convey("D", func() { + output.output += "D " + }) + }() + }) + + expectEqual(t, "A B C R A D R ", output.output) +} + +func TestInfiniteLoopWithTrailingFail(t *testing.T) { + done := make(chan int) + + go func() { + Convey("This fails", t, func() { + Convey("and this is run", func() { + So(true, ShouldEqual, true) + }) + + /* And this prevents the whole block to be marked as run */ + So(false, ShouldEqual, true) + }) + + done <- 1 + }() + + select { + case <-done: + return + case <-time.After(1 * time.Millisecond): + t.Fail() + } +} + +func TestOutermostResetInvokedForGrandchildren(t *testing.T) { + output := prepare() + + Convey("A", t, func() { + output += "A " + + Reset(func() { + output += "rA " + }) + + Convey("B", func() { + output += "B " + + Reset(func() { + output += "rB " + }) + + Convey("C", func() { + output += "C " + + Reset(func() { + output += "rC " + }) + }) + + Convey("D", func() { + output += "D " + + Reset(func() { + output += "rD " + }) + }) + }) + }) + + expectEqual(t, "A B C rC rB rA A B D rD rB rA ", output) +} + +func TestFailureOption(t *testing.T) { + output := prepare() + + Convey("A", t, FailureHalts, func() { + output += "A " + So(true, ShouldEqual, true) + output += "B " + So(false, ShouldEqual, true) + output += "C " + }) + + expectEqual(t, "A B ", output) +} + +func TestFailureOption2(t *testing.T) { + output := prepare() + + Convey("A", t, func() { + output += "A " + So(true, ShouldEqual, true) + output += "B " + So(false, ShouldEqual, true) + output += "C " + }) + + expectEqual(t, "A B ", output) +} + +func TestFailureOption3(t *testing.T) { + output := prepare() + + Convey("A", t, FailureContinues, func() { + output += "A " + So(true, ShouldEqual, true) + output += "B " + So(false, ShouldEqual, true) + output += "C " + }) + + expectEqual(t, "A B C ", output) +} + +func TestFailureOptionInherit(t *testing.T) { + output := prepare() + + Convey("A", t, FailureContinues, func() { + output += "A1 " + So(false, ShouldEqual, true) + output += "A2 " + + Convey("B", func() { + output += "B1 " + So(true, ShouldEqual, true) + output += "B2 " + So(false, ShouldEqual, true) + output += "B3 " + }) + }) + + expectEqual(t, "A1 A2 B1 B2 B3 ", output) +} + +func TestFailureOptionInherit2(t *testing.T) { + output := prepare() + + Convey("A", t, FailureHalts, func() { + output += "A1 " + So(false, ShouldEqual, true) + output += "A2 " + + Convey("B", func() { + output += "A1 " + So(true, ShouldEqual, true) + output += "A2 " + So(false, ShouldEqual, true) + output += "A3 " + }) + }) + + expectEqual(t, "A1 ", output) +} + +func TestFailureOptionInherit3(t *testing.T) { + output := prepare() + + Convey("A", t, FailureHalts, func() { + output += "A1 " + So(true, ShouldEqual, true) + output += "A2 " + + Convey("B", func() { + output += "B1 " + So(true, ShouldEqual, true) + output += "B2 " + So(false, ShouldEqual, true) + output += "B3 " + }) + }) + + expectEqual(t, "A1 A2 B1 B2 ", output) +} + +func TestFailureOptionNestedOverride(t *testing.T) { + output := prepare() + + Convey("A", t, FailureContinues, func() { + output += "A " + So(false, ShouldEqual, true) + output += "B " + + Convey("C", FailureHalts, func() { + output += "C " + So(true, ShouldEqual, true) + output += "D " + So(false, ShouldEqual, true) + output += "E " + }) + }) + + expectEqual(t, "A B C D ", output) +} + +func TestFailureOptionNestedOverride2(t *testing.T) { + output := prepare() + + Convey("A", t, FailureHalts, func() { + output += "A " + So(true, ShouldEqual, true) + output += "B " + + Convey("C", FailureContinues, func() { + output += "C " + So(true, ShouldEqual, true) + output += "D " + So(false, ShouldEqual, true) + output += "E " + }) + }) + + expectEqual(t, "A B C D E ", output) +} + +func TestMultipleInvocationInheritance(t *testing.T) { + output := prepare() + + Convey("A", t, FailureHalts, func() { + output += "A1 " + So(true, ShouldEqual, true) + output += "A2 " + + Convey("B", FailureContinues, func() { + output += "B1 " + So(true, ShouldEqual, true) + output += "B2 " + So(false, ShouldEqual, true) + output += "B3 " + }) + + Convey("C", func() { + output += "C1 " + So(true, ShouldEqual, true) + output += "C2 " + So(false, ShouldEqual, true) + output += "C3 " + }) + }) + + expectEqual(t, "A1 A2 B1 B2 B3 A1 A2 C1 C2 ", output) +} + +func TestMultipleInvocationInheritance2(t *testing.T) { + output := prepare() + + Convey("A", t, FailureContinues, func() { + output += "A1 " + So(true, ShouldEqual, true) + output += "A2 " + So(false, ShouldEqual, true) + output += "A3 " + + Convey("B", FailureHalts, func() { + output += "B1 " + So(true, ShouldEqual, true) + output += "B2 " + So(false, ShouldEqual, true) + output += "B3 " + }) + + Convey("C", func() { + output += "C1 " + So(true, ShouldEqual, true) + output += "C2 " + So(false, ShouldEqual, true) + output += "C3 " + }) + }) + + expectEqual(t, "A1 A2 A3 B1 B2 A1 A2 A3 C1 C2 C3 ", output) +} + +func TestSetDefaultFailureMode(t *testing.T) { + output := prepare() + + SetDefaultFailureMode(FailureContinues) // the default is normally FailureHalts + defer SetDefaultFailureMode(FailureHalts) + + Convey("A", t, func() { + output += "A1 " + So(true, ShouldBeFalse) + output += "A2 " + }) + + expectEqual(t, "A1 A2 ", output) +} + +func prepare() string { + testReporter = newNilReporter() + return "" +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/nilReporter.go b/vendor/github.com/smartystreets/goconvey/convey/nilReporter.go new file mode 100644 index 0000000..c6a361f --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/nilReporter.go @@ -0,0 +1,15 @@ +package convey + +import ( + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" +) + +type nilReporter struct{} + +func (self *nilReporter) BeginStory(story *reporting.StoryReport) {} +func (self *nilReporter) Enter(scope *reporting.ScopeReport) {} +func (self *nilReporter) Report(report *reporting.AssertionResult) {} +func (self *nilReporter) Exit() {} +func (self *nilReporter) EndStory() {} +func (self *nilReporter) Write(p []byte) (int, error) { return len(p), nil } +func newNilReporter() *nilReporter { return &nilReporter{} } diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/console.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/console.go new file mode 100644 index 0000000..7bf67db --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/console.go @@ -0,0 +1,16 @@ +package reporting + +import ( + "fmt" + "io" +) + +type console struct{} + +func (self *console) Write(p []byte) (n int, err error) { + return fmt.Print(string(p)) +} + +func NewConsole() io.Writer { + return new(console) +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go new file mode 100644 index 0000000..a37d001 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go @@ -0,0 +1,5 @@ +// Package reporting contains internal functionality related +// to console reporting and output. Although this package has +// exported names is not intended for public consumption. See the +// examples package for how to use this project. +package reporting diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go new file mode 100644 index 0000000..47d57c6 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go @@ -0,0 +1,40 @@ +package reporting + +import "fmt" + +type dot struct{ out *Printer } + +func (self *dot) BeginStory(story *StoryReport) {} + +func (self *dot) Enter(scope *ScopeReport) {} + +func (self *dot) Report(report *AssertionResult) { + if report.Error != nil { + fmt.Print(redColor) + self.out.Insert(dotError) + } else if report.Failure != "" { + fmt.Print(yellowColor) + self.out.Insert(dotFailure) + } else if report.Skipped { + fmt.Print(yellowColor) + self.out.Insert(dotSkip) + } else { + fmt.Print(greenColor) + self.out.Insert(dotSuccess) + } + fmt.Print(resetColor) +} + +func (self *dot) Exit() {} + +func (self *dot) EndStory() {} + +func (self *dot) Write(content []byte) (written int, err error) { + return len(content), nil // no-op +} + +func NewDotReporter(out *Printer) *dot { + self := new(dot) + self.out = out + return self +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/dot_test.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/dot_test.go new file mode 100644 index 0000000..a8d20d4 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/dot_test.go @@ -0,0 +1,40 @@ +package reporting + +import ( + "errors" + "testing" +) + +func TestDotReporterAssertionPrinting(t *testing.T) { + monochrome() + file := newMemoryFile() + printer := NewPrinter(file) + reporter := NewDotReporter(printer) + + reporter.Report(NewSuccessReport()) + reporter.Report(NewFailureReport("failed")) + reporter.Report(NewErrorReport(errors.New("error"))) + reporter.Report(NewSkipReport()) + + expected := dotSuccess + dotFailure + dotError + dotSkip + + if file.buffer != expected { + t.Errorf("\nExpected: '%s'\nActual: '%s'", expected, file.buffer) + } +} + +func TestDotReporterOnlyReportsAssertions(t *testing.T) { + monochrome() + file := newMemoryFile() + printer := NewPrinter(file) + reporter := NewDotReporter(printer) + + reporter.BeginStory(nil) + reporter.Enter(nil) + reporter.Exit() + reporter.EndStory() + + if file.buffer != "" { + t.Errorf("\nExpected: '(blank)'\nActual: '%s'", file.buffer) + } +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go new file mode 100644 index 0000000..c396e16 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go @@ -0,0 +1,33 @@ +package reporting + +type gotestReporter struct{ test T } + +func (self *gotestReporter) BeginStory(story *StoryReport) { + self.test = story.Test +} + +func (self *gotestReporter) Enter(scope *ScopeReport) {} + +func (self *gotestReporter) Report(r *AssertionResult) { + if !passed(r) { + self.test.Fail() + } +} + +func (self *gotestReporter) Exit() {} + +func (self *gotestReporter) EndStory() { + self.test = nil +} + +func (self *gotestReporter) Write(content []byte) (written int, err error) { + return len(content), nil // no-op +} + +func NewGoTestReporter() *gotestReporter { + return new(gotestReporter) +} + +func passed(r *AssertionResult) bool { + return r.Error == nil && r.Failure == "" +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest_test.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest_test.go new file mode 100644 index 0000000..fda1894 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest_test.go @@ -0,0 +1,66 @@ +package reporting + +import "testing" + +func TestReporterReceivesSuccessfulReport(t *testing.T) { + reporter := NewGoTestReporter() + test := new(fakeTest) + reporter.BeginStory(NewStoryReport(test)) + reporter.Report(NewSuccessReport()) + + if test.failed { + t.Errorf("Should have have marked test as failed--the report reflected success.") + } +} + +func TestReporterReceivesFailureReport(t *testing.T) { + reporter := NewGoTestReporter() + test := new(fakeTest) + reporter.BeginStory(NewStoryReport(test)) + reporter.Report(NewFailureReport("This is a failure.")) + + if !test.failed { + t.Errorf("Test should have been marked as failed (but it wasn't).") + } +} + +func TestReporterReceivesErrorReport(t *testing.T) { + reporter := NewGoTestReporter() + test := new(fakeTest) + reporter.BeginStory(NewStoryReport(test)) + reporter.Report(NewErrorReport("This is an error.")) + + if !test.failed { + t.Errorf("Test should have been marked as failed (but it wasn't).") + } +} + +func TestReporterIsResetAtTheEndOfTheStory(t *testing.T) { + defer catch(t) + reporter := NewGoTestReporter() + test := new(fakeTest) + reporter.BeginStory(NewStoryReport(test)) + reporter.EndStory() + + reporter.Report(NewSuccessReport()) +} + +func TestReporterNoopMethods(t *testing.T) { + reporter := NewGoTestReporter() + reporter.Enter(NewScopeReport("title")) + reporter.Exit() +} + +func catch(t *testing.T) { + if r := recover(); r != nil { + t.Log("Getting to this point means we've passed (because we caught a panic appropriately).") + } +} + +type fakeTest struct { + failed bool +} + +func (self *fakeTest) Fail() { + self.failed = true +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/init.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/init.go new file mode 100644 index 0000000..44a1332 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/init.go @@ -0,0 +1,97 @@ +package reporting + +import ( + "fmt" + "os" + "runtime" + "strings" +) + +func init() { + if !isXterm() { + monochrome() + } + + if runtime.GOOS == "windows" { + success, failure, error_ = dotSuccess, dotFailure, dotError + } +} + +func BuildJsonReporter() Reporter { + out := NewPrinter(NewConsole()) + return NewReporters( + NewGoTestReporter(), + NewJsonReporter(out)) +} +func BuildDotReporter() Reporter { + out := NewPrinter(NewConsole()) + return NewReporters( + NewGoTestReporter(), + NewDotReporter(out), + NewProblemReporter(out), + consoleStatistics) +} +func BuildStoryReporter() Reporter { + out := NewPrinter(NewConsole()) + return NewReporters( + NewGoTestReporter(), + NewStoryReporter(out), + NewProblemReporter(out), + consoleStatistics) +} +func BuildSilentReporter() Reporter { + out := NewPrinter(NewConsole()) + return NewReporters( + NewGoTestReporter(), + NewProblemReporter(out)) +} + +var ( + newline = "\n" + success = "✔" + failure = "✘" + error_ = "🔥" + skip = "⚠" + dotSuccess = "." + dotFailure = "x" + dotError = "E" + dotSkip = "S" + errorTemplate = "* %s \nLine %d: - %v \n%s\n" + failureTemplate = "* %s \nLine %d:\n%s\n" +) + +var ( + greenColor = "\033[32m" + yellowColor = "\033[33m" + redColor = "\033[31m" + resetColor = "\033[0m" +) + +var consoleStatistics = NewStatisticsReporter(NewPrinter(NewConsole())) + +func SuppressConsoleStatistics() { consoleStatistics.Suppress() } +func PrintConsoleStatistics() { consoleStatistics.PrintSummary() } + +// QuiteMode disables all console output symbols. This is only meant to be used +// for tests that are internal to goconvey where the output is distracting or +// otherwise not needed in the test output. +func QuietMode() { + success, failure, error_, skip, dotSuccess, dotFailure, dotError, dotSkip = "", "", "", "", "", "", "", "" +} + +func monochrome() { + greenColor, yellowColor, redColor, resetColor = "", "", "", "" +} + +func isXterm() bool { + env := fmt.Sprintf("%v", os.Environ()) + return strings.Contains(env, " TERM=isXterm") || + strings.Contains(env, " TERM=xterm") +} + +// This interface allows us to pass the *testing.T struct +// throughout the internals of this tool without ever +// having to import the "testing" package. +type T interface { + Fail() +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/json.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/json.go new file mode 100644 index 0000000..f852697 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/json.go @@ -0,0 +1,88 @@ +// TODO: under unit test + +package reporting + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +type JsonReporter struct { + out *Printer + currentKey []string + current *ScopeResult + index map[string]*ScopeResult + scopes []*ScopeResult +} + +func (self *JsonReporter) depth() int { return len(self.currentKey) } + +func (self *JsonReporter) BeginStory(story *StoryReport) {} + +func (self *JsonReporter) Enter(scope *ScopeReport) { + self.currentKey = append(self.currentKey, scope.Title) + ID := strings.Join(self.currentKey, "|") + if _, found := self.index[ID]; !found { + next := newScopeResult(scope.Title, self.depth(), scope.File, scope.Line) + self.scopes = append(self.scopes, next) + self.index[ID] = next + } + self.current = self.index[ID] +} + +func (self *JsonReporter) Report(report *AssertionResult) { + self.current.Assertions = append(self.current.Assertions, report) +} + +func (self *JsonReporter) Exit() { + self.currentKey = self.currentKey[:len(self.currentKey)-1] +} + +func (self *JsonReporter) EndStory() { + self.report() + self.reset() +} +func (self *JsonReporter) report() { + scopes := []string{} + for _, scope := range self.scopes { + serialized, err := json.Marshal(scope) + if err != nil { + self.out.Println(jsonMarshalFailure) + panic(err) + } + var buffer bytes.Buffer + json.Indent(&buffer, serialized, "", " ") + scopes = append(scopes, buffer.String()) + } + self.out.Print(fmt.Sprintf("%s\n%s,\n%s\n", OpenJson, strings.Join(scopes, ","), CloseJson)) +} +func (self *JsonReporter) reset() { + self.scopes = []*ScopeResult{} + self.index = map[string]*ScopeResult{} + self.currentKey = nil +} + +func (self *JsonReporter) Write(content []byte) (written int, err error) { + self.current.Output += string(content) + return len(content), nil +} + +func NewJsonReporter(out *Printer) *JsonReporter { + self := new(JsonReporter) + self.out = out + self.reset() + return self +} + +const OpenJson = ">->->OPEN-JSON->->->" // "⌦" +const CloseJson = "<-<-<-CLOSE-JSON<-<-<" // "⌫" +const jsonMarshalFailure = ` + +GOCONVEY_JSON_MARSHALL_FAILURE: There was an error when attempting to convert test results to JSON. +Please file a bug report and reference the code that caused this failure if possible. + +Here's the panic: + +` diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go new file mode 100644 index 0000000..6d4a879 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go @@ -0,0 +1,57 @@ +package reporting + +import ( + "fmt" + "io" + "strings" +) + +type Printer struct { + out io.Writer + prefix string +} + +func (self *Printer) Println(message string, values ...interface{}) { + formatted := self.format(message, values...) + newline + self.out.Write([]byte(formatted)) +} + +func (self *Printer) Print(message string, values ...interface{}) { + formatted := self.format(message, values...) + self.out.Write([]byte(formatted)) +} + +func (self *Printer) Insert(text string) { + self.out.Write([]byte(text)) +} + +func (self *Printer) format(message string, values ...interface{}) string { + var formatted string + if len(values) == 0 { + formatted = self.prefix + message + } else { + formatted = self.prefix + fmt.Sprintf(message, values...) + } + indented := strings.Replace(formatted, newline, newline+self.prefix, -1) + return strings.TrimRight(indented, space) +} + +func (self *Printer) Indent() { + self.prefix += pad +} + +func (self *Printer) Dedent() { + if len(self.prefix) >= padLength { + self.prefix = self.prefix[:len(self.prefix)-padLength] + } +} + +func NewPrinter(out io.Writer) *Printer { + self := new(Printer) + self.out = out + return self +} + +const space = " " +const pad = space + space +const padLength = len(pad) diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/printer_test.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/printer_test.go new file mode 100644 index 0000000..94202d5 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/printer_test.go @@ -0,0 +1,181 @@ +package reporting + +import "testing" + +func TestPrint(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const expected = "Hello, World!" + + printer.Print(expected) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintFormat(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + template := "Hi, %s" + name := "Ralph" + expected := "Hi, Ralph" + + printer.Print(template, name) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintPreservesEncodedStrings(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const expected = "= -> %3D" + printer.Print(expected) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintln(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const expected = "Hello, World!" + + printer.Println(expected) + + if file.buffer != expected+"\n" { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintlnFormat(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + template := "Hi, %s" + name := "Ralph" + expected := "Hi, Ralph\n" + + printer.Println(template, name) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintlnPreservesEncodedStrings(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const expected = "= -> %3D" + printer.Println(expected) + + if file.buffer != expected+"\n" { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintIndented(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const message = "Hello, World!\nGoodbye, World!" + const expected = " Hello, World!\n Goodbye, World!" + + printer.Indent() + printer.Print(message) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintDedented(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const expected = "Hello, World!\nGoodbye, World!" + + printer.Indent() + printer.Dedent() + printer.Print(expected) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintlnIndented(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const message = "Hello, World!\nGoodbye, World!" + const expected = " Hello, World!\n Goodbye, World!\n" + + printer.Indent() + printer.Println(message) + + if file.buffer != expected { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestPrintlnDedented(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + const expected = "Hello, World!\nGoodbye, World!" + + printer.Indent() + printer.Dedent() + printer.Println(expected) + + if file.buffer != expected+"\n" { + t.Errorf("Expected '%s' to equal '%s'.", expected, file.buffer) + } +} + +func TestDedentTooFarShouldNotPanic(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Error("Should not have panicked!") + } + }() + file := newMemoryFile() + printer := NewPrinter(file) + + printer.Dedent() + + t.Log("Getting to this point without panicking means we passed.") +} + +func TestInsert(t *testing.T) { + file := newMemoryFile() + printer := NewPrinter(file) + + printer.Indent() + printer.Print("Hi") + printer.Insert(" there") + printer.Dedent() + + expected := " Hi there" + if file.buffer != expected { + t.Errorf("Should have written '%s' but instead wrote '%s'.", expected, file.buffer) + } +} + +////////////////// memoryFile //////////////////// + +type memoryFile struct { + buffer string +} + +func (self *memoryFile) Write(p []byte) (n int, err error) { + self.buffer += string(p) + return len(p), nil +} + +func (self *memoryFile) String() string { + return self.buffer +} + +func newMemoryFile() *memoryFile { + return new(memoryFile) +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go new file mode 100644 index 0000000..c610ba8 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go @@ -0,0 +1,68 @@ +package reporting + +import "fmt" + +type problem struct { + out *Printer + errors []*AssertionResult + failures []*AssertionResult +} + +func (self *problem) BeginStory(story *StoryReport) {} + +func (self *problem) Enter(scope *ScopeReport) {} + +func (self *problem) Report(report *AssertionResult) { + if report.Error != nil { + self.errors = append(self.errors, report) + } else if report.Failure != "" { + self.failures = append(self.failures, report) + } +} + +func (self *problem) Exit() {} + +func (self *problem) EndStory() { + self.show(self.showErrors, redColor) + self.show(self.showFailures, yellowColor) + self.prepareForNextStory() +} +func (self *problem) show(display func(), color string) { + fmt.Print(color) + display() + fmt.Print(resetColor) + self.out.Dedent() +} +func (self *problem) showErrors() { + for i, e := range self.errors { + if i == 0 { + self.out.Println("\nErrors:\n") + self.out.Indent() + } + self.out.Println(errorTemplate, e.File, e.Line, e.Error, e.StackTrace) + } +} +func (self *problem) showFailures() { + for i, f := range self.failures { + if i == 0 { + self.out.Println("\nFailures:\n") + self.out.Indent() + } + self.out.Println(failureTemplate, f.File, f.Line, f.Failure) + } +} + +func (self *problem) Write(content []byte) (written int, err error) { + return len(content), nil // no-op +} + +func NewProblemReporter(out *Printer) *problem { + self := new(problem) + self.out = out + self.prepareForNextStory() + return self +} +func (self *problem) prepareForNextStory() { + self.errors = []*AssertionResult{} + self.failures = []*AssertionResult{} +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/problems_test.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/problems_test.go new file mode 100644 index 0000000..92f0ca3 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/problems_test.go @@ -0,0 +1,51 @@ +package reporting + +import ( + "strings" + "testing" +) + +func TestNoopProblemReporterActions(t *testing.T) { + file, reporter := setup() + reporter.BeginStory(nil) + reporter.Enter(nil) + reporter.Exit() + expected := "" + actual := file.String() + if expected != actual { + t.Errorf("Expected: '(blank)'\nActual: '%s'", actual) + } +} + +func TestReporterPrintsFailuresAndErrorsAtTheEndOfTheStory(t *testing.T) { + file, reporter := setup() + reporter.Report(NewFailureReport("failed")) + reporter.Report(NewErrorReport("error")) + reporter.Report(NewSuccessReport()) + reporter.EndStory() + + result := file.String() + if !strings.Contains(result, "Errors:\n") { + t.Errorf("Expected errors, found none.") + } + if !strings.Contains(result, "Failures:\n") { + t.Errorf("Expected failures, found none.") + } + + // Each stack trace looks like: `* /path/to/file.go`, so look for `* `. + // With go 1.4+ there is a line in some stack traces that looks like this: + // `testing.(*M).Run(0x2082d60a0, 0x25b7c0)` + // So we can't just look for "*" anymore. + problemCount := strings.Count(result, "* ") + if problemCount != 2 { + t.Errorf("Expected one failure and one error (total of 2 '*' characters). Got %d", problemCount) + } +} + +func setup() (file *memoryFile, reporter *problem) { + monochrome() + file = newMemoryFile() + printer := NewPrinter(file) + reporter = NewProblemReporter(printer) + return +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go new file mode 100644 index 0000000..cce6c5e --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go @@ -0,0 +1,39 @@ +package reporting + +import "io" + +type Reporter interface { + BeginStory(story *StoryReport) + Enter(scope *ScopeReport) + Report(r *AssertionResult) + Exit() + EndStory() + io.Writer +} + +type reporters struct{ collection []Reporter } + +func (self *reporters) BeginStory(s *StoryReport) { self.foreach(func(r Reporter) { r.BeginStory(s) }) } +func (self *reporters) Enter(s *ScopeReport) { self.foreach(func(r Reporter) { r.Enter(s) }) } +func (self *reporters) Report(a *AssertionResult) { self.foreach(func(r Reporter) { r.Report(a) }) } +func (self *reporters) Exit() { self.foreach(func(r Reporter) { r.Exit() }) } +func (self *reporters) EndStory() { self.foreach(func(r Reporter) { r.EndStory() }) } + +func (self *reporters) Write(contents []byte) (written int, err error) { + self.foreach(func(r Reporter) { + written, err = r.Write(contents) + }) + return written, err +} + +func (self *reporters) foreach(action func(Reporter)) { + for _, r := range self.collection { + action(r) + } +} + +func NewReporters(collection ...Reporter) *reporters { + self := new(reporters) + self.collection = collection + return self +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter_test.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter_test.go new file mode 100644 index 0000000..4e5caf6 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter_test.go @@ -0,0 +1,94 @@ +package reporting + +import ( + "runtime" + "testing" +) + +func TestEachNestedReporterReceivesTheCallFromTheContainingReporter(t *testing.T) { + fake1 := newFakeReporter() + fake2 := newFakeReporter() + reporter := NewReporters(fake1, fake2) + + reporter.BeginStory(nil) + assertTrue(t, fake1.begun) + assertTrue(t, fake2.begun) + + reporter.Enter(NewScopeReport("scope")) + assertTrue(t, fake1.entered) + assertTrue(t, fake2.entered) + + reporter.Report(NewSuccessReport()) + assertTrue(t, fake1.reported) + assertTrue(t, fake2.reported) + + reporter.Exit() + assertTrue(t, fake1.exited) + assertTrue(t, fake2.exited) + + reporter.EndStory() + assertTrue(t, fake1.ended) + assertTrue(t, fake2.ended) + + content := []byte("hi") + written, err := reporter.Write(content) + assertTrue(t, fake1.written) + assertTrue(t, fake2.written) + assertEqual(t, written, len(content)) + assertNil(t, err) + +} + +func assertTrue(t *testing.T, value bool) { + if !value { + _, _, line, _ := runtime.Caller(1) + t.Errorf("Value should have been true (but was false). See line %d", line) + } +} + +func assertEqual(t *testing.T, expected, actual int) { + if actual != expected { + _, _, line, _ := runtime.Caller(1) + t.Errorf("Value should have been %d (but was %d). See line %d", expected, actual, line) + } +} + +func assertNil(t *testing.T, err error) { + if err != nil { + _, _, line, _ := runtime.Caller(1) + t.Errorf("Error should have been (but wasn't). See line %d", err, line) + } +} + +type fakeReporter struct { + begun bool + entered bool + reported bool + exited bool + ended bool + written bool +} + +func newFakeReporter() *fakeReporter { + return &fakeReporter{} +} + +func (self *fakeReporter) BeginStory(story *StoryReport) { + self.begun = true +} +func (self *fakeReporter) Enter(scope *ScopeReport) { + self.entered = true +} +func (self *fakeReporter) Report(report *AssertionResult) { + self.reported = true +} +func (self *fakeReporter) Exit() { + self.exited = true +} +func (self *fakeReporter) EndStory() { + self.ended = true +} +func (self *fakeReporter) Write(content []byte) (int, error) { + self.written = true + return len(content), nil +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey new file mode 100644 index 0000000..7998285 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey @@ -0,0 +1,2 @@ +#ignore +-timeout=1s diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go new file mode 100644 index 0000000..06f7885 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go @@ -0,0 +1,177 @@ +package reporting + +import ( + "encoding/json" + "fmt" + "runtime" + "strings" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/gotest" +) + +////////////////// ScopeReport //////////////////// + +type ScopeReport struct { + Title string + File string + Line int +} + +func NewScopeReport(title string) *ScopeReport { + file, line, _ := gotest.ResolveExternalCaller() + self := new(ScopeReport) + self.Title = title + self.File = file + self.Line = line + return self +} + +////////////////// ScopeResult //////////////////// + +type ScopeResult struct { + Title string + File string + Line int + Depth int + Assertions []*AssertionResult + Output string +} + +func newScopeResult(title string, depth int, file string, line int) *ScopeResult { + self := new(ScopeResult) + self.Title = title + self.Depth = depth + self.File = file + self.Line = line + self.Assertions = []*AssertionResult{} + return self +} + +/////////////////// StoryReport ///////////////////// + +type StoryReport struct { + Test T + Name string + File string + Line int +} + +func NewStoryReport(test T) *StoryReport { + file, line, name := gotest.ResolveExternalCaller() + name = removePackagePath(name) + self := new(StoryReport) + self.Test = test + self.Name = name + self.File = file + self.Line = line + return self +} + +// name comes in looking like "github.com/smartystreets/goconvey/examples.TestName". +// We only want the stuff after the last '.', which is the name of the test function. +func removePackagePath(name string) string { + parts := strings.Split(name, ".") + return parts[len(parts)-1] +} + +/////////////////// FailureView //////////////////////// + +type FailureView struct { + Message string + Expected string + Actual string +} + +////////////////////AssertionResult ////////////////////// + +type AssertionResult struct { + File string + Line int + Expected string + Actual string + Failure string + Error interface{} + StackTrace string + Skipped bool +} + +func NewFailureReport(failure string) *AssertionResult { + report := new(AssertionResult) + report.File, report.Line = caller() + report.StackTrace = stackTrace() + parseFailure(failure, report) + return report +} +func parseFailure(failure string, report *AssertionResult) { + view := new(FailureView) + err := json.Unmarshal([]byte(failure), view) + if err == nil { + report.Failure = view.Message + report.Expected = view.Expected + report.Actual = view.Actual + } else { + report.Failure = failure + } +} +func NewErrorReport(err interface{}) *AssertionResult { + report := new(AssertionResult) + report.File, report.Line = caller() + report.StackTrace = fullStackTrace() + report.Error = fmt.Sprintf("%v", err) + return report +} +func NewSuccessReport() *AssertionResult { + return new(AssertionResult) +} +func NewSkipReport() *AssertionResult { + report := new(AssertionResult) + report.File, report.Line = caller() + report.StackTrace = fullStackTrace() + report.Skipped = true + return report +} + +func caller() (file string, line int) { + file, line, _ = gotest.ResolveExternalCaller() + return +} + +func stackTrace() string { + buffer := make([]byte, 1024*64) + n := runtime.Stack(buffer, false) + return removeInternalEntries(string(buffer[:n])) +} +func fullStackTrace() string { + buffer := make([]byte, 1024*64) + n := runtime.Stack(buffer, true) + return removeInternalEntries(string(buffer[:n])) +} +func removeInternalEntries(stack string) string { + lines := strings.Split(stack, newline) + filtered := []string{} + for _, line := range lines { + if !isExternal(line) { + filtered = append(filtered, line) + } + } + return strings.Join(filtered, newline) +} +func isExternal(line string) bool { + for _, p := range internalPackages { + if strings.Contains(line, p) { + return true + } + } + return false +} + +// NOTE: any new packages that host goconvey packages will need to be added here! +// An alternative is to scan the goconvey directory and then exclude stuff like +// the examples package but that's nasty too. +var internalPackages = []string{ + "goconvey/assertions", + "goconvey/convey", + "goconvey/execution", + "goconvey/gotest", + "goconvey/reporting", +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go new file mode 100644 index 0000000..28e1d20 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go @@ -0,0 +1,89 @@ +package reporting + +import "fmt" + +func (self *statistics) BeginStory(story *StoryReport) {} + +func (self *statistics) Enter(scope *ScopeReport) {} + +func (self *statistics) Report(report *AssertionResult) { + if !self.failing && report.Failure != "" { + self.failing = true + } + if !self.erroring && report.Error != nil { + self.erroring = true + } + if report.Skipped { + self.skipped += 1 + } else { + self.total++ + } +} + +func (self *statistics) Exit() {} + +func (self *statistics) EndStory() { + if !self.suppressed { + self.PrintSummary() + } +} + +func (self *statistics) Suppress() { + self.suppressed = true +} + +func (self *statistics) PrintSummary() { + self.reportAssertions() + self.reportSkippedSections() + self.completeReport() +} +func (self *statistics) reportAssertions() { + self.decideColor() + self.out.Print("\n%d total %s", self.total, plural("assertion", self.total)) +} +func (self *statistics) decideColor() { + if self.failing && !self.erroring { + fmt.Print(yellowColor) + } else if self.erroring { + fmt.Print(redColor) + } else { + fmt.Print(greenColor) + } +} +func (self *statistics) reportSkippedSections() { + if self.skipped > 0 { + fmt.Print(yellowColor) + self.out.Print(" (one or more sections skipped)") + } +} +func (self *statistics) completeReport() { + fmt.Print(resetColor) + self.out.Print("\n") + self.out.Print("\n") +} + +func (self *statistics) Write(content []byte) (written int, err error) { + return len(content), nil // no-op +} + +func NewStatisticsReporter(out *Printer) *statistics { + self := statistics{} + self.out = out + return &self +} + +type statistics struct { + out *Printer + total int + failing bool + erroring bool + skipped int + suppressed bool +} + +func plural(word string, count int) string { + if count == 1 { + return word + } + return word + "s" +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/story.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/story.go new file mode 100644 index 0000000..9e73c97 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/story.go @@ -0,0 +1,73 @@ +// TODO: in order for this reporter to be completely honest +// we need to retrofit to be more like the json reporter such that: +// 1. it maintains ScopeResult collections, which count assertions +// 2. it reports only after EndStory(), so that all tick marks +// are placed near the appropriate title. +// 3. Under unit test + +package reporting + +import ( + "fmt" + "strings" +) + +type story struct { + out *Printer + titlesById map[string]string + currentKey []string +} + +func (self *story) BeginStory(story *StoryReport) {} + +func (self *story) Enter(scope *ScopeReport) { + self.out.Indent() + + self.currentKey = append(self.currentKey, scope.Title) + ID := strings.Join(self.currentKey, "|") + + if _, found := self.titlesById[ID]; !found { + self.out.Println("") + self.out.Print(scope.Title) + self.out.Insert(" ") + self.titlesById[ID] = scope.Title + } +} + +func (self *story) Report(report *AssertionResult) { + if report.Error != nil { + fmt.Print(redColor) + self.out.Insert(error_) + } else if report.Failure != "" { + fmt.Print(yellowColor) + self.out.Insert(failure) + } else if report.Skipped { + fmt.Print(yellowColor) + self.out.Insert(skip) + } else { + fmt.Print(greenColor) + self.out.Insert(success) + } + fmt.Print(resetColor) +} + +func (self *story) Exit() { + self.out.Dedent() + self.currentKey = self.currentKey[:len(self.currentKey)-1] +} + +func (self *story) EndStory() { + self.titlesById = make(map[string]string) + self.out.Println("\n") +} + +func (self *story) Write(content []byte) (written int, err error) { + return len(content), nil // no-op +} + +func NewStoryReporter(out *Printer) *story { + self := new(story) + self.out = out + self.titlesById = make(map[string]string) + return self +} diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting_hooks_test.go b/vendor/github.com/smartystreets/goconvey/convey/reporting_hooks_test.go new file mode 100644 index 0000000..669c087 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/reporting_hooks_test.go @@ -0,0 +1,317 @@ +package convey + +import ( + "fmt" + "net/http" + "net/http/httptest" + "path" + "runtime" + "strconv" + "strings" + "testing" + + "github.com/moul/ssh2docker/vendor/github.com/smartystreets/goconvey/convey/reporting" +) + +func TestSingleScopeReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + So(1, ShouldEqual, 1) + }) + + expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory()) +} + +func TestNestedScopeReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + Convey("B", func() { + So(1, ShouldEqual, 1) + }) + }) + + expectEqual(t, "Begin|A|B|Success|Exit|Exit|End", myReporter.wholeStory()) +} + +func TestFailureReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + So(1, ShouldBeNil) + }) + + expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory()) +} + +func TestFirstFailureEndsScopeExecution(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + So(1, ShouldBeNil) + So(nil, ShouldBeNil) + }) + + expectEqual(t, "Begin|A|Failure|Exit|End", myReporter.wholeStory()) +} + +func TestComparisonFailureDeserializedAndReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + So("hi", ShouldEqual, "bye") + }) + + expectEqual(t, "Begin|A|Failure(bye/hi)|Exit|End", myReporter.wholeStory()) +} + +func TestNestedFailureReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + Convey("B", func() { + So(2, ShouldBeNil) + }) + }) + + expectEqual(t, "Begin|A|B|Failure|Exit|Exit|End", myReporter.wholeStory()) +} + +func TestSuccessAndFailureReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + So(nil, ShouldBeNil) + So(1, ShouldBeNil) + }) + + expectEqual(t, "Begin|A|Success|Failure|Exit|End", myReporter.wholeStory()) +} + +func TestIncompleteActionReportedAsSkipped(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + Convey("B", nil) + }) + + expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory()) +} + +func TestSkippedConveyReportedAsSkipped(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + SkipConvey("B", func() { + So(1, ShouldEqual, 1) + }) + }) + + expectEqual(t, "Begin|A|B|Skipped|Exit|Exit|End", myReporter.wholeStory()) +} + +func TestMultipleSkipsAreReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + Convey("0", func() { + So(nil, ShouldBeNil) + }) + + SkipConvey("1", func() {}) + SkipConvey("2", func() {}) + + Convey("3", nil) + Convey("4", nil) + + Convey("5", func() { + So(nil, ShouldBeNil) + }) + }) + + expected := "Begin" + + "|A|0|Success|Exit|Exit" + + "|A|1|Skipped|Exit|Exit" + + "|A|2|Skipped|Exit|Exit" + + "|A|3|Skipped|Exit|Exit" + + "|A|4|Skipped|Exit|Exit" + + "|A|5|Success|Exit|Exit" + + "|End" + + expectEqual(t, expected, myReporter.wholeStory()) +} + +func TestSkippedAssertionIsNotReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + SkipSo(1, ShouldEqual, 1) + }) + + expectEqual(t, "Begin|A|Skipped|Exit|End", myReporter.wholeStory()) +} + +func TestMultipleSkippedAssertionsAreNotReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + SkipSo(1, ShouldEqual, 1) + So(1, ShouldEqual, 1) + SkipSo(1, ShouldEqual, 1) + }) + + expectEqual(t, "Begin|A|Skipped|Success|Skipped|Exit|End", myReporter.wholeStory()) +} + +func TestErrorByManualPanicReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + panic("Gopher alert!") + }) + + expectEqual(t, "Begin|A|Error|Exit|End", myReporter.wholeStory()) +} + +func TestIterativeConveysReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + for x := 0; x < 3; x++ { + Convey(strconv.Itoa(x), func() { + So(x, ShouldEqual, x) + }) + } + }) + + expectEqual(t, "Begin|A|0|Success|Exit|Exit|A|1|Success|Exit|Exit|A|2|Success|Exit|Exit|End", myReporter.wholeStory()) +} + +func TestNestedIterativeConveysReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func() { + for x := 0; x < 3; x++ { + Convey(strconv.Itoa(x), func() { + for y := 0; y < 3; y++ { + Convey("< "+strconv.Itoa(y), func() { + So(x, ShouldBeLessThan, y) + }) + } + }) + } + }) + + expectEqual(t, ("Begin|" + + "A|0|< 0|Failure|Exit|Exit|Exit|" + + "A|0|< 1|Success|Exit|Exit|Exit|" + + "A|0|< 2|Success|Exit|Exit|Exit|" + + "A|1|< 0|Failure|Exit|Exit|Exit|" + + "A|1|< 1|Failure|Exit|Exit|Exit|" + + "A|1|< 2|Success|Exit|Exit|Exit|" + + "A|2|< 0|Failure|Exit|Exit|Exit|" + + "A|2|< 1|Failure|Exit|Exit|Exit|" + + "A|2|< 2|Failure|Exit|Exit|Exit|" + + "End"), myReporter.wholeStory()) +} + +func TestEmbeddedAssertionReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + Convey("A", test, func(c C) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.So(r.FormValue("msg"), ShouldEqual, "ping") + })) + http.DefaultClient.Get(ts.URL + "?msg=ping") + }) + + expectEqual(t, "Begin|A|Success|Exit|End", myReporter.wholeStory()) +} + +func TestEmbeddedContextHelperReported(t *testing.T) { + myReporter, test := setupFakeReporter() + + helper := func(c C) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Convey("Embedded", func() { + So(r.FormValue("msg"), ShouldEqual, "ping") + }) + }) + } + + Convey("A", test, func(c C) { + ts := httptest.NewServer(helper(c)) + http.DefaultClient.Get(ts.URL + "?msg=ping") + }) + + expectEqual(t, "Begin|A|Embedded|Success|Exit|Exit|End", myReporter.wholeStory()) +} + +func expectEqual(t *testing.T, expected interface{}, actual interface{}) { + if expected != actual { + _, file, line, _ := runtime.Caller(1) + t.Errorf("Expected '%v' to be '%v' but it wasn't. See '%s' at line %d.", + actual, expected, path.Base(file), line) + } +} + +func setupFakeReporter() (*fakeReporter, *fakeGoTest) { + myReporter := new(fakeReporter) + myReporter.calls = []string{} + testReporter = myReporter + return myReporter, new(fakeGoTest) +} + +type fakeReporter struct { + calls []string +} + +func (self *fakeReporter) BeginStory(story *reporting.StoryReport) { + self.calls = append(self.calls, "Begin") +} + +func (self *fakeReporter) Enter(scope *reporting.ScopeReport) { + self.calls = append(self.calls, scope.Title) +} + +func (self *fakeReporter) Report(report *reporting.AssertionResult) { + if report.Error != nil { + self.calls = append(self.calls, "Error") + } else if report.Failure != "" { + message := "Failure" + if report.Expected != "" || report.Actual != "" { + message += fmt.Sprintf("(%s/%s)", report.Expected, report.Actual) + } + self.calls = append(self.calls, message) + } else if report.Skipped { + self.calls = append(self.calls, "Skipped") + } else { + self.calls = append(self.calls, "Success") + } +} + +func (self *fakeReporter) Exit() { + self.calls = append(self.calls, "Exit") +} + +func (self *fakeReporter) EndStory() { + self.calls = append(self.calls, "End") +} + +func (self *fakeReporter) Write(content []byte) (int, error) { + return len(content), nil // no-op +} + +func (self *fakeReporter) wholeStory() string { + return strings.Join(self.calls, "|") +} + +//////////////////////////////// + +type fakeGoTest struct{} + +func (self *fakeGoTest) Fail() {} +func (self *fakeGoTest) Fatalf(format string, args ...interface{}) {} + +var test t = new(fakeGoTest) diff --git a/vendor/github.com/smartystreets/goconvey/convey/story_conventions_test.go b/vendor/github.com/smartystreets/goconvey/convey/story_conventions_test.go new file mode 100644 index 0000000..ff3a4e6 --- /dev/null +++ b/vendor/github.com/smartystreets/goconvey/convey/story_conventions_test.go @@ -0,0 +1,184 @@ +package convey + +import ( + "reflect" + "testing" +) + +func expectPanic(t *testing.T, f string) interface{} { + r := recover() + if r != nil { + if cp, ok := r.(*conveyErr); ok { + if cp.fmt != f { + t.Error("Incorrect panic message.") + } + } else { + t.Errorf("Incorrect panic type. %s", reflect.TypeOf(r)) + } + } else { + t.Error("Expected panic but none occured") + } + return r +} + +func TestMissingTopLevelGoTestReferenceCausesPanic(t *testing.T) { + output := map[string]bool{} + + defer expectEqual(t, false, output["good"]) + defer expectPanic(t, missingGoTest) + + Convey("Hi", func() { + output["bad"] = true // this shouldn't happen + }) +} + +func requireGoTestReference(t *testing.T) { + err := recover() + if err == nil { + t.Error("We should have recovered a panic here (because of a missing *testing.T reference)!") + } else { + expectEqual(t, missingGoTest, err) + } +} + +func TestMissingTopLevelGoTestReferenceAfterGoodExample(t *testing.T) { + output := map[string]bool{} + + defer func() { + expectEqual(t, true, output["good"]) + expectEqual(t, false, output["bad"]) + }() + defer expectPanic(t, missingGoTest) + + Convey("Good example", t, func() { + output["good"] = true + }) + + Convey("Bad example", func() { + output["bad"] = true // shouldn't happen + }) +} + +func TestExtraReferencePanics(t *testing.T) { + output := map[string]bool{} + + defer expectEqual(t, false, output["bad"]) + defer expectPanic(t, extraGoTest) + + Convey("Good example", t, func() { + Convey("Bad example - passing in *testing.T a second time!", t, func() { + output["bad"] = true // shouldn't happen + }) + }) +} + +func TestParseRegistrationMissingRequiredElements(t *testing.T) { + defer expectPanic(t, parseError) + + Convey() +} + +func TestParseRegistration_MissingNameString(t *testing.T) { + defer expectPanic(t, parseError) + + Convey(func() {}) +} + +func TestParseRegistration_MissingActionFunc(t *testing.T) { + defer expectPanic(t, parseError) + + Convey("Hi there", 12345) +} + +func TestFailureModeNoContext(t *testing.T) { + Convey("Foo", t, func() { + done := make(chan int, 1) + go func() { + defer func() { done <- 1 }() + defer expectPanic(t, noStackContext) + So(len("I have no context"), ShouldBeGreaterThan, 0) + }() + <-done + }) +} + +func TestFailureModeDuplicateSuite(t *testing.T) { + Convey("cool", t, func() { + defer expectPanic(t, multipleIdenticalConvey) + + Convey("dup", nil) + Convey("dup", nil) + }) +} + +func TestFailureModeIndeterminentSuiteNames(t *testing.T) { + defer expectPanic(t, differentConveySituations) + + name := "bob" + Convey("cool", t, func() { + for i := 0; i < 3; i++ { + Convey(name, func() {}) + name += "bob" + } + }) +} + +func TestFailureModeNestedIndeterminentSuiteNames(t *testing.T) { + defer expectPanic(t, differentConveySituations) + + name := "bob" + Convey("cool", t, func() { + Convey("inner", func() { + for i := 0; i < 3; i++ { + Convey(name, func() {}) + name += "bob" + } + }) + }) +} + +func TestFailureModeParameterButMissing(t *testing.T) { + defer expectPanic(t, parseError) + + prepare() + + Convey("Foobar", t, FailureHalts) +} + +func TestFailureModeParameterWithAction(t *testing.T) { + prepare() + + Convey("Foobar", t, FailureHalts, func() {}) +} + +func TestExtraConveyParameters(t *testing.T) { + defer expectPanic(t, parseError) + + prepare() + + Convey("Foobar", t, FailureHalts, func() {}, "This is not supposed to be here") +} + +func TestExtraConveyParameters2(t *testing.T) { + defer expectPanic(t, parseError) + + prepare() + + Convey("Foobar", t, func() {}, "This is not supposed to be here") +} + +func TestExtraConveyParameters3(t *testing.T) { + defer expectPanic(t, parseError) + + output := prepare() + + Convey("A", t, func() { + output += "A " + + Convey("B", func() { + output += "B " + }, "This is not supposed to be here") + }) + + expectEqual(t, "A ", output) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go new file mode 100644 index 0000000..fbf03f4 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -0,0 +1,895 @@ +package assert + +import ( + "bufio" + "bytes" + "fmt" + "math" + "reflect" + "regexp" + "runtime" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) +} + +// Comparison a custom function that returns true on success and false on failure +type Comparison func() (success bool) + +/* + Helper functions +*/ + +// ObjectsAreEqual determines if two objects are considered equal. +// +// This function does no assertion of any kind. +func ObjectsAreEqual(expected, actual interface{}) bool { + + if expected == nil || actual == nil { + return expected == actual + } + + if reflect.DeepEqual(expected, actual) { + return true + } + + return false + +} + +// ObjectsAreEqualValues gets whether two objects are equal, or if their +// values are equal. +func ObjectsAreEqualValues(expected, actual interface{}) bool { + if ObjectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + expectedValue := reflect.ValueOf(expected) + if expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + if reflect.DeepEqual(actual, expectedValue.Convert(actualType).Interface()) { + return true + } + } + + return false +} + +/* CallerInfo is necessary because the assert functions use the testing object +internally, causing it to print the file:line of the assert method, rather than where +the problem actually occured in calling code.*/ + +// CallerInfo returns an array of strings containing the file and line number +// of each stack frame leading from the current test to the assert call that +// failed. +func CallerInfo() []string { + + pc := uintptr(0) + file := "" + line := 0 + ok := false + name := "" + + callers := []string{} + for i := 0; ; i++ { + pc, file, line, ok = runtime.Caller(i) + if !ok { + return nil + } + + // This is a huge edge case, but it will panic if this is the case, see #180 + if file == "" { + break + } + + parts := strings.Split(file, "/") + dir := parts[len(parts)-2] + file = parts[len(parts)-1] + if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + } + + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() + // Drop the package + segments := strings.Split(name, ".") + name = segments[len(segments)-1] + if isTest(name, "Test") || + isTest(name, "Benchmark") || + isTest(name, "Example") { + break + } + } + + return callers +} + +// Stolen from the `go test` tool. +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +// getWhitespaceString returns a string that is long enough to overwrite the default +// output from the go testing framework. +func getWhitespaceString() string { + + _, file, line, ok := runtime.Caller(1) + if !ok { + return "" + } + parts := strings.Split(file, "/") + file = parts[len(parts)-1] + + return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) + +} + +func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { + if len(msgAndArgs) == 0 || msgAndArgs == nil { + return "" + } + if len(msgAndArgs) == 1 { + return msgAndArgs[0].(string) + } + if len(msgAndArgs) > 1 { + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } + return "" +} + +// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's +// test printing (see inner comment for specifics) +func indentMessageLines(message string, tabs int) string { + outBuf := new(bytes.Buffer) + + for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { + if i != 0 { + outBuf.WriteRune('\n') + } + for ii := 0; ii < tabs; ii++ { + outBuf.WriteRune('\t') + // Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter + // by 1 prematurely. + if ii == 0 && i > 0 { + ii++ + } + } + outBuf.WriteString(scanner.Text()) + } + + return outBuf.String() +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + + message := messageFromMsgAndArgs(msgAndArgs...) + + errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t") + if len(message) > 0 { + t.Errorf("\r%s\r\tError Trace:\t%s\n"+ + "\r\tError:%s\n"+ + "\r\tMessages:\t%s\n\r", + getWhitespaceString(), + errorTrace, + indentMessageLines(failureMessage, 2), + message) + } else { + t.Errorf("\r%s\r\tError Trace:\t%s\n"+ + "\r\tError:%s\n\r", + getWhitespaceString(), + errorTrace, + indentMessageLines(failureMessage, 2)) + } + + return false +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + + interfaceType := reflect.TypeOf(interfaceObject).Elem() + + if !reflect.TypeOf(object).Implements(interfaceType) { + return Fail(t, fmt.Sprintf("Object must implement %v", interfaceType), msgAndArgs...) + } + + return true + +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + + if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { + return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) + } + + return true +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + if !ObjectsAreEqual(expected, actual) { + return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ + " != %#v (actual)", expected, actual), msgAndArgs...) + } + + return true + +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + if !ObjectsAreEqualValues(expected, actual) { + return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+ + " != %#v (actual)", expected, actual), msgAndArgs...) + } + + return true + +} + +// Exactly asserts that two objects are equal is value and type. +// +// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, "Types expected to match exactly", "%v != %v", aType, bType) + } + + return Equal(t, expected, actual, msgAndArgs...) + +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + + success := true + + if object == nil { + success = false + } else { + value := reflect.ValueOf(object) + kind := value.Kind() + if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + success = false + } + } + + if !success { + Fail(t, "Expected value not to be nil.", msgAndArgs...) + } + + return success +} + +// isNil checks if a specified object is nil or not, without Failing. +func isNil(object interface{}) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + kind := value.Kind() + if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + return true + } + + return false +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if isNil(object) { + return true + } + return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) +} + +var zeros = []interface{}{ + int(0), + int8(0), + int16(0), + int32(0), + int64(0), + uint(0), + uint8(0), + uint16(0), + uint32(0), + uint64(0), + float32(0), + float64(0), +} + +// isEmpty gets whether the specified object is considered empty or not. +func isEmpty(object interface{}) bool { + + if object == nil { + return true + } else if object == "" { + return true + } else if object == false { + return true + } + + for _, v := range zeros { + if object == v { + return true + } + } + + objValue := reflect.ValueOf(object) + + switch objValue.Kind() { + case reflect.Map: + fallthrough + case reflect.Slice, reflect.Chan: + { + return (objValue.Len() == 0) + } + case reflect.Ptr: + { + switch object.(type) { + case *time.Time: + return object.(*time.Time).IsZero() + default: + return false + } + } + } + return false +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +// +// Returns whether the assertion was successful (true) or not (false). +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + + pass := isEmpty(object) + if !pass { + Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + + pass := !isEmpty(object) + if !pass { + Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// getLen try to get length of object. +// return (false, 0) if impossible. +func getLen(x interface{}) (ok bool, length int) { + v := reflect.ValueOf(x) + defer func() { + if e := recover(); e != nil { + ok = false + } + }() + return true, v.Len() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3, "The size of slice is not 3") +// +// Returns whether the assertion was successful (true) or not (false). +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { + ok, l := getLen(object) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) + } + + if l != length { + return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + } + return true +} + +// True asserts that the specified value is true. +// +// assert.True(t, myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { + + if value != true { + return Fail(t, "Should be true", msgAndArgs...) + } + + return true + +} + +// False asserts that the specified value is true. +// +// assert.False(t, myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { + + if value != false { + return Fail(t, "Should be false", msgAndArgs...) + } + + return true + +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + + if ObjectsAreEqual(expected, actual) { + return Fail(t, "Should not be equal", msgAndArgs...) + } + + return true + +} + +// containsElement try loop over the list check if the list includes the element. +// return (false, false) if impossible. +// return (true, false) if element was not found. +// return (true, true) if element was found. +func includeElement(list interface{}, element interface{}) (ok, found bool) { + + listValue := reflect.ValueOf(list) + elementValue := reflect.ValueOf(element) + defer func() { + if e := recover(); e != nil { + ok = false + found = false + } + }() + + if reflect.TypeOf(list).Kind() == reflect.String { + return true, strings.Contains(listValue.String(), elementValue.String()) + } + + for i := 0; i < listValue.Len(); i++ { + if ObjectsAreEqual(listValue.Index(i).Interface(), element) { + return true, true + } + } + return true, false + +} + +// Contains asserts that the specified string or list(array, slice...) contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") +// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") +// +// Returns whether the assertion was successful (true) or not (false). +func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + + ok, found := includeElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if !found { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// NotContains asserts that the specified string or list(array, slice...) does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") +// +// Returns whether the assertion was successful (true) or not (false). +func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + + ok, found := includeElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if found { + return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { + result := comp() + if !result { + Fail(t, "Condition failed!", msgAndArgs...) + } + return result +} + +// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics +// methods, and represents a simple func that takes no arguments, and returns nothing. +type PanicTestFunc func() + +// didPanic returns true if the function passed to it panics. Otherwise, it returns false. +func didPanic(f PanicTestFunc) (bool, interface{}) { + + didPanic := false + var message interface{} + func() { + + defer func() { + if message = recover(); message != nil { + didPanic = true + } + }() + + // call the target function + f() + + }() + + return didPanic, message + +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + + if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + } + + return true +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + + if funcDidPanic, panicValue := didPanic(f); funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) + } + + return true +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") +// +// Returns whether the assertion was successful (true) or not (false). +func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + + dt := expected.Sub(actual) + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +func toFloat(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + } + + if math.IsNaN(af) { + return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...) + } + + if math.IsNaN(bf) { + return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) + } + + dt := af - bf + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) + if !result { + return result + } + } + + return true +} + +// min(|expected|, |actual|) * epsilon +func calcEpsilonDelta(expected, actual interface{}, epsilon float64) float64 { + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + // invalid input + return 0 + } + + if af < 0 { + af = -af + } + if bf < 0 { + bf = -bf + } + var delta float64 + if af < bf { + delta = af * epsilon + } else { + delta = bf * epsilon + } + return delta +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + delta := calcEpsilonDelta(expected, actual, epsilon) + + return InDelta(t, expected, actual, delta, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares two slices. +func InEpsilonSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) + if !result { + return result + } + } + + return true +} + +/* + Errors +*/ + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { + if isNil(err) { + return true + } + + return Fail(t, fmt.Sprintf("No error is expected but got %v", err), msgAndArgs...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { + + message := messageFromMsgAndArgs(msgAndArgs...) + return NotNil(t, err, "An error is expected but got nil. %s", message) + +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { + + message := messageFromMsgAndArgs(msgAndArgs...) + if !NotNil(t, theError, "An error is expected but got nil. %s", message) { + return false + } + s := "An error with value \"%s\" is expected but got \"%s\". %s" + return Equal(t, theError.Error(), errString, + s, errString, theError.Error(), message) +} + +// matchRegexp return true if a specified regexp matches a string. +func matchRegexp(rx interface{}, str interface{}) bool { + + var r *regexp.Regexp + if rr, ok := rx.(*regexp.Regexp); ok { + r = rr + } else { + r = regexp.MustCompile(fmt.Sprint(rx)) + } + + return (r.FindStringIndex(fmt.Sprint(str)) != nil) + +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + + match := matchRegexp(rx, str) + + if !match { + Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) + } + + return match +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + match := matchRegexp(rx, str) + + if match { + Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) + } + + return !match + +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions_test.go b/vendor/github.com/stretchr/testify/assert/assertions_test.go new file mode 100644 index 0000000..36c671e --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertions_test.go @@ -0,0 +1,813 @@ +package assert + +import ( + "errors" + "io" + "math" + "regexp" + "testing" + "time" +) + +// AssertionTesterInterface defines an interface to be used for testing assertion methods +type AssertionTesterInterface interface { + TestMethod() +} + +// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface +type AssertionTesterConformingObject struct { +} + +func (a *AssertionTesterConformingObject) TestMethod() { +} + +// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface +type AssertionTesterNonConformingObject struct { +} + +func TestObjectsAreEqual(t *testing.T) { + + if !ObjectsAreEqual("Hello World", "Hello World") { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual(123, 123) { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual(123.5, 123.5) { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual([]byte("Hello World"), []byte("Hello World")) { + t.Error("objectsAreEqual should return true") + } + if !ObjectsAreEqual(nil, nil) { + t.Error("objectsAreEqual should return true") + } + if ObjectsAreEqual(map[int]int{5: 10}, map[int]int{10: 20}) { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual('x', "x") { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual("x", 'x') { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual(0, 0.1) { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual(0.1, 0) { + t.Error("objectsAreEqual should return false") + } + if ObjectsAreEqual(uint32(10), int32(10)) { + t.Error("objectsAreEqual should return false") + } + if !ObjectsAreEqualValues(uint32(10), int32(10)) { + t.Error("ObjectsAreEqualValues should return true") + } + +} + +func TestImplements(t *testing.T) { + + mockT := new(testing.T) + + if !Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { + t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") + } + if Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { + t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") + } + +} + +func TestIsType(t *testing.T) { + + mockT := new(testing.T) + + if !IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } + if IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + +} + +func TestEqual(t *testing.T) { + + mockT := new(testing.T) + + if !Equal(mockT, "Hello World", "Hello World") { + t.Error("Equal should return true") + } + if !Equal(mockT, 123, 123) { + t.Error("Equal should return true") + } + if !Equal(mockT, 123.5, 123.5) { + t.Error("Equal should return true") + } + if !Equal(mockT, []byte("Hello World"), []byte("Hello World")) { + t.Error("Equal should return true") + } + if !Equal(mockT, nil, nil) { + t.Error("Equal should return true") + } + if !Equal(mockT, int32(123), int32(123)) { + t.Error("Equal should return true") + } + if !Equal(mockT, uint64(123), uint64(123)) { + t.Error("Equal should return true") + } + +} + +func TestNotNil(t *testing.T) { + + mockT := new(testing.T) + + if !NotNil(mockT, new(AssertionTesterConformingObject)) { + t.Error("NotNil should return true: object is not nil") + } + if NotNil(mockT, nil) { + t.Error("NotNil should return false: object is nil") + } + +} + +func TestNil(t *testing.T) { + + mockT := new(testing.T) + + if !Nil(mockT, nil) { + t.Error("Nil should return true: object is nil") + } + if Nil(mockT, new(AssertionTesterConformingObject)) { + t.Error("Nil should return false: object is not nil") + } + +} + +func TestTrue(t *testing.T) { + + mockT := new(testing.T) + + if !True(mockT, true) { + t.Error("True should return true") + } + if True(mockT, false) { + t.Error("True should return false") + } + +} + +func TestFalse(t *testing.T) { + + mockT := new(testing.T) + + if !False(mockT, false) { + t.Error("False should return true") + } + if False(mockT, true) { + t.Error("False should return false") + } + +} + +func TestExactly(t *testing.T) { + + mockT := new(testing.T) + + a := float32(1) + b := float64(1) + c := float32(1) + d := float32(2) + + if Exactly(mockT, a, b) { + t.Error("Exactly should return false") + } + if Exactly(mockT, a, d) { + t.Error("Exactly should return false") + } + if !Exactly(mockT, a, c) { + t.Error("Exactly should return true") + } + + if Exactly(mockT, nil, a) { + t.Error("Exactly should return false") + } + if Exactly(mockT, a, nil) { + t.Error("Exactly should return false") + } + +} + +func TestNotEqual(t *testing.T) { + + mockT := new(testing.T) + + if !NotEqual(mockT, "Hello World", "Hello World!") { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, 123, 1234) { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, 123.5, 123.55) { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, []byte("Hello World"), []byte("Hello World!")) { + t.Error("NotEqual should return true") + } + if !NotEqual(mockT, nil, new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return true") + } + funcA := func() int { return 23 } + funcB := func() int { return 42 } + if !NotEqual(mockT, funcA, funcB) { + t.Error("NotEqual should return true") + } + + if NotEqual(mockT, "Hello World", "Hello World") { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, 123, 123) { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, 123.5, 123.5) { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, []byte("Hello World"), []byte("Hello World")) { + t.Error("NotEqual should return false") + } + if NotEqual(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return false") + } +} + +type A struct { + Name, Value string +} + +func TestContains(t *testing.T) { + + mockT := new(testing.T) + list := []string{"Foo", "Bar"} + complexList := []*A{ + {"b", "c"}, + {"d", "e"}, + {"g", "h"}, + {"j", "k"}, + } + + if !Contains(mockT, "Hello World", "Hello") { + t.Error("Contains should return true: \"Hello World\" contains \"Hello\"") + } + if Contains(mockT, "Hello World", "Salut") { + t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"") + } + + if !Contains(mockT, list, "Bar") { + t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Bar\"") + } + if Contains(mockT, list, "Salut") { + t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"") + } + if !Contains(mockT, complexList, &A{"g", "h"}) { + t.Error("Contains should return true: complexList contains {\"g\", \"h\"}") + } + if Contains(mockT, complexList, &A{"g", "e"}) { + t.Error("Contains should return false: complexList contains {\"g\", \"e\"}") + } +} + +func TestNotContains(t *testing.T) { + + mockT := new(testing.T) + list := []string{"Foo", "Bar"} + + if !NotContains(mockT, "Hello World", "Hello!") { + t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"") + } + if NotContains(mockT, "Hello World", "Hello") { + t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"") + } + + if !NotContains(mockT, list, "Foo!") { + t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"") + } + if NotContains(mockT, list, "Foo") { + t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + +} + +func Test_includeElement(t *testing.T) { + + list1 := []string{"Foo", "Bar"} + list2 := []int{1, 2} + + ok, found := includeElement("Hello World", "World") + True(t, ok) + True(t, found) + + ok, found = includeElement(list1, "Foo") + True(t, ok) + True(t, found) + + ok, found = includeElement(list1, "Bar") + True(t, ok) + True(t, found) + + ok, found = includeElement(list2, 1) + True(t, ok) + True(t, found) + + ok, found = includeElement(list2, 2) + True(t, ok) + True(t, found) + + ok, found = includeElement(list1, "Foo!") + True(t, ok) + False(t, found) + + ok, found = includeElement(list2, 3) + True(t, ok) + False(t, found) + + ok, found = includeElement(list2, "1") + True(t, ok) + False(t, found) + + ok, found = includeElement(1433, "1") + False(t, ok) + False(t, found) + +} + +func TestCondition(t *testing.T) { + mockT := new(testing.T) + + if !Condition(mockT, func() bool { return true }, "Truth") { + t.Error("Condition should return true") + } + + if Condition(mockT, func() bool { return false }, "Lie") { + t.Error("Condition should return false") + } + +} + +func TestDidPanic(t *testing.T) { + + if funcDidPanic, _ := didPanic(func() { + panic("Panic!") + }); !funcDidPanic { + t.Error("didPanic should return true") + } + + if funcDidPanic, _ := didPanic(func() { + }); funcDidPanic { + t.Error("didPanic should return false") + } + +} + +func TestPanics(t *testing.T) { + + mockT := new(testing.T) + + if !Panics(mockT, func() { + panic("Panic!") + }) { + t.Error("Panics should return true") + } + + if Panics(mockT, func() { + }) { + t.Error("Panics should return false") + } + +} + +func TestNotPanics(t *testing.T) { + + mockT := new(testing.T) + + if !NotPanics(mockT, func() { + }) { + t.Error("NotPanics should return true") + } + + if NotPanics(mockT, func() { + panic("Panic!") + }) { + t.Error("NotPanics should return false") + } + +} + +func TestNoError(t *testing.T) { + + mockT := new(testing.T) + + // start with a nil error + var err error + + True(t, NoError(mockT, err), "NoError should return True for nil arg") + + // now set an error + err = errors.New("some error") + + False(t, NoError(mockT, err), "NoError with error should return False") + +} + +func TestError(t *testing.T) { + + mockT := new(testing.T) + + // start with a nil error + var err error + + False(t, Error(mockT, err), "Error should return False for nil arg") + + // now set an error + err = errors.New("some error") + + True(t, Error(mockT, err), "Error with error should return True") + +} + +func TestEqualError(t *testing.T) { + mockT := new(testing.T) + + // start with a nil error + var err error + False(t, EqualError(mockT, err, ""), + "EqualError should return false for nil arg") + + // now set an error + err = errors.New("some error") + False(t, EqualError(mockT, err, "Not some error"), + "EqualError should return false for different error string") + True(t, EqualError(mockT, err, "some error"), + "EqualError should return true") +} + +func Test_isEmpty(t *testing.T) { + + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + True(t, isEmpty("")) + True(t, isEmpty(nil)) + True(t, isEmpty([]string{})) + True(t, isEmpty(0)) + True(t, isEmpty(int32(0))) + True(t, isEmpty(int64(0))) + True(t, isEmpty(false)) + True(t, isEmpty(map[string]string{})) + True(t, isEmpty(new(time.Time))) + True(t, isEmpty(make(chan struct{}))) + False(t, isEmpty("something")) + False(t, isEmpty(errors.New("something"))) + False(t, isEmpty([]string{"something"})) + False(t, isEmpty(1)) + False(t, isEmpty(true)) + False(t, isEmpty(map[string]string{"Hello": "World"})) + False(t, isEmpty(chWithValue)) + +} + +func TestEmpty(t *testing.T) { + + mockT := new(testing.T) + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + True(t, Empty(mockT, ""), "Empty string is empty") + True(t, Empty(mockT, nil), "Nil is empty") + True(t, Empty(mockT, []string{}), "Empty string array is empty") + True(t, Empty(mockT, 0), "Zero int value is empty") + True(t, Empty(mockT, false), "False value is empty") + True(t, Empty(mockT, make(chan struct{})), "Channel without values is empty") + + False(t, Empty(mockT, "something"), "Non Empty string is not empty") + False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty") + False(t, Empty(mockT, []string{"something"}), "Non empty string array is not empty") + False(t, Empty(mockT, 1), "Non-zero int value is not empty") + False(t, Empty(mockT, true), "True value is not empty") + False(t, Empty(mockT, chWithValue), "Channel with values is not empty") +} + +func TestNotEmpty(t *testing.T) { + + mockT := new(testing.T) + chWithValue := make(chan struct{}, 1) + chWithValue <- struct{}{} + + False(t, NotEmpty(mockT, ""), "Empty string is empty") + False(t, NotEmpty(mockT, nil), "Nil is empty") + False(t, NotEmpty(mockT, []string{}), "Empty string array is empty") + False(t, NotEmpty(mockT, 0), "Zero int value is empty") + False(t, NotEmpty(mockT, false), "False value is empty") + False(t, NotEmpty(mockT, make(chan struct{})), "Channel without values is empty") + + True(t, NotEmpty(mockT, "something"), "Non Empty string is not empty") + True(t, NotEmpty(mockT, errors.New("something")), "Non nil object is not empty") + True(t, NotEmpty(mockT, []string{"something"}), "Non empty string array is not empty") + True(t, NotEmpty(mockT, 1), "Non-zero int value is not empty") + True(t, NotEmpty(mockT, true), "True value is not empty") + True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty") +} + +func Test_getLen(t *testing.T) { + falseCases := []interface{}{ + nil, + 0, + true, + false, + 'A', + struct{}{}, + } + for _, v := range falseCases { + ok, l := getLen(v) + False(t, ok, "Expected getLen fail to get length of %#v", v) + Equal(t, 0, l, "getLen should return 0 for %#v", v) + } + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + trueCases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range trueCases { + ok, l := getLen(c.v) + True(t, ok, "Expected getLen success to get length of %#v", c.v) + Equal(t, c.l, l) + } +} + +func TestLen(t *testing.T) { + mockT := new(testing.T) + + False(t, Len(mockT, nil, 0), "nil does not have length") + False(t, Len(mockT, 0, 0), "int does not have length") + False(t, Len(mockT, true, 0), "true does not have length") + False(t, Len(mockT, false, 0), "false does not have length") + False(t, Len(mockT, 'A', 0), "Rune does not have length") + False(t, Len(mockT, struct{}{}, 0), "Struct does not have length") + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + + cases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range cases { + True(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l) + } + + cases = []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 4}, + {[...]int{1, 2, 3}, 2}, + {"ABC", 2}, + {map[int]int{1: 2, 2: 4, 3: 6}, 4}, + {ch, 2}, + + {[]int{}, 1}, + {map[int]int{}, 1}, + {make(chan int), 1}, + + {[]int(nil), 1}, + {map[int]int(nil), 1}, + {(chan int)(nil), 1}, + } + + for _, c := range cases { + False(t, Len(mockT, c.v, c.l), "%#v have %d items", c.v, c.l) + } +} + +func TestWithinDuration(t *testing.T) { + + mockT := new(testing.T) + a := time.Now() + b := a.Add(10 * time.Second) + + True(t, WithinDuration(mockT, a, b, 10*time.Second), "A 10s difference is within a 10s time difference") + True(t, WithinDuration(mockT, b, a, 10*time.Second), "A 10s difference is within a 10s time difference") + + False(t, WithinDuration(mockT, a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") + + False(t, WithinDuration(mockT, a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") + + False(t, WithinDuration(mockT, a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") + False(t, WithinDuration(mockT, b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") +} + +func TestInDelta(t *testing.T) { + mockT := new(testing.T) + + True(t, InDelta(mockT, 1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, InDelta(mockT, 1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, InDelta(mockT, 1, 2, 1), "|1 - 2| <= 1") + False(t, InDelta(mockT, 1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, InDelta(mockT, 2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, InDelta(mockT, "", nil, 1), "Expected non numerals to fail") + False(t, InDelta(mockT, 42, math.NaN(), 0.01), "Expected NaN for actual to fail") + False(t, InDelta(mockT, math.NaN(), 42, 0.01), "Expected NaN for expected to fail") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, InDelta(mockT, tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} + +func TestInDeltaSlice(t *testing.T) { + mockT := new(testing.T) + + True(t, InDeltaSlice(mockT, + []float64{1.001, 0.999}, + []float64{1, 1}, + 0.1), "{1.001, 0.009} is element-wise close to {1, 1} in delta=0.1") + + True(t, InDeltaSlice(mockT, + []float64{1, 2}, + []float64{0, 3}, + 1), "{1, 2} is element-wise close to {0, 3} in delta=1") + + False(t, InDeltaSlice(mockT, + []float64{1, 2}, + []float64{0, 3}, + 0.1), "{1, 2} is not element-wise close to {0, 3} in delta=0.1") + + False(t, InDeltaSlice(mockT, "", nil, 1), "Expected non numeral slices to fail") +} + +func TestInEpsilon(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), uint16(2), .001}, + {2.1, 2.2, 0.1}, + {2.2, 2.1, 0.1}, + {-2.1, -2.2, 0.1}, + {-2.2, -2.1, 0.1}, + {uint64(100), uint8(101), 0.01}, + {0.1, -0.1, 2}, + } + + for _, tc := range cases { + True(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + + cases = []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), int16(-2), .001}, + {uint64(100), uint8(102), 0.01}, + {2.1, 2.2, 0.001}, + {2.2, 2.1, 0.001}, + {2.1, -2.2, 1}, + {2.1, "bla-bla", 0}, + {0.1, -0.1, 1.99}, + } + + for _, tc := range cases { + False(t, InEpsilon(mockT, tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + +} + +func TestInEpsilonSlice(t *testing.T) { + mockT := new(testing.T) + + True(t, InEpsilonSlice(mockT, + []float64{2.2, 2.0}, + []float64{2.1, 2.1}, + 0.06), "{2.2, 2.0} is element-wise close to {2.1, 2.1} in espilon=0.06") + + False(t, InEpsilonSlice(mockT, + []float64{2.2, 2.0}, + []float64{2.1, 2.1}, + 0.04), "{2.2, 2.0} is not element-wise close to {2.1, 2.1} in espilon=0.04") + + False(t, InEpsilonSlice(mockT, "", nil, 1), "Expected non numeral slices to fail") +} + +func TestRegexp(t *testing.T) { + mockT := new(testing.T) + + cases := []struct { + rx, str string + }{ + {"^start", "start of the line"}, + {"end$", "in the end"}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, + } + + for _, tc := range cases { + True(t, Regexp(mockT, tc.rx, tc.str)) + True(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + False(t, NotRegexp(mockT, tc.rx, tc.str)) + False(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + } + + cases = []struct { + rx, str string + }{ + {"^asdfastart", "Not the start of the line"}, + {"end$", "in the end."}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, + } + + for _, tc := range cases { + False(t, Regexp(mockT, tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, Regexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + True(t, NotRegexp(mockT, tc.rx, tc.str)) + True(t, NotRegexp(mockT, regexp.MustCompile(tc.rx), tc.str)) + } +} + +func testAutogeneratedFunction() { + defer func() { + if err := recover(); err == nil { + panic("did not panic") + } + CallerInfo() + }() + t := struct { + io.Closer + }{} + var c io.Closer + c = t + c.Close() +} + +func TestCallerInfoWithAutogeneratedFunctions(t *testing.T) { + NotPanics(t, func() { + testAutogeneratedFunction() + }) +} diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go new file mode 100644 index 0000000..d10a8a1 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -0,0 +1,154 @@ +// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. +// +// Example Usage +// +// The following is a complete example using assert in a standard test function: +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// if you assert many times, use the format below: +// +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) +// +// func TestSomething(t *testing.T) { +// assert := assert.New(t) +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(a, b, "The two words should be the same.") +// } +// +// Assertions +// +// Assertions allow you to easily write test code, and are global funcs in the `assert` package. +// All assertion functions take, as the first argument, the `*testing.T` object provided by the +// testing framework. This allows the assertion funcs to write the failings and other details to +// the correct place. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +// +// Here is an overview of the assert functions: +// +// assert.Equal(t, expected, actual [, message [, format-args]]) +// +// assert.EqualValues(t, expected, actual [, message [, format-args]]) +// +// assert.NotEqual(t, notExpected, actual [, message [, format-args]]) +// +// assert.True(t, actualBool [, message [, format-args]]) +// +// assert.False(t, actualBool [, message [, format-args]]) +// +// assert.Nil(t, actualObject [, message [, format-args]]) +// +// assert.NotNil(t, actualObject [, message [, format-args]]) +// +// assert.Empty(t, actualObject [, message [, format-args]]) +// +// assert.NotEmpty(t, actualObject [, message [, format-args]]) +// +// assert.Len(t, actualObject, expectedLength, [, message [, format-args]]) +// +// assert.Error(t, errorObject [, message [, format-args]]) +// +// assert.NoError(t, errorObject [, message [, format-args]]) +// +// assert.EqualError(t, theError, errString [, message [, format-args]]) +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject) [,message [, format-args]]) +// +// assert.IsType(t, expectedObject, actualObject [, message [, format-args]]) +// +// assert.Contains(t, stringOrSlice, substringOrElement [, message [, format-args]]) +// +// assert.NotContains(t, stringOrSlice, substringOrElement [, message [, format-args]]) +// +// assert.Panics(t, func(){ +// +// // call code that should panic +// +// } [, message [, format-args]]) +// +// assert.NotPanics(t, func(){ +// +// // call code that should not panic +// +// } [, message [, format-args]]) +// +// assert.WithinDuration(t, timeA, timeB, deltaTime, [, message [, format-args]]) +// +// assert.InDelta(t, numA, numB, delta, [, message [, format-args]]) +// +// assert.InEpsilon(t, numA, numB, epsilon, [, message [, format-args]]) +// +// assert package contains Assertions object. it has assertion methods. +// +// Here is an overview of the assert functions: +// assert.Equal(expected, actual [, message [, format-args]]) +// +// assert.EqualValues(expected, actual [, message [, format-args]]) +// +// assert.NotEqual(notExpected, actual [, message [, format-args]]) +// +// assert.True(actualBool [, message [, format-args]]) +// +// assert.False(actualBool [, message [, format-args]]) +// +// assert.Nil(actualObject [, message [, format-args]]) +// +// assert.NotNil(actualObject [, message [, format-args]]) +// +// assert.Empty(actualObject [, message [, format-args]]) +// +// assert.NotEmpty(actualObject [, message [, format-args]]) +// +// assert.Len(actualObject, expectedLength, [, message [, format-args]]) +// +// assert.Error(errorObject [, message [, format-args]]) +// +// assert.NoError(errorObject [, message [, format-args]]) +// +// assert.EqualError(theError, errString [, message [, format-args]]) +// +// assert.Implements((*MyInterface)(nil), new(MyObject) [,message [, format-args]]) +// +// assert.IsType(expectedObject, actualObject [, message [, format-args]]) +// +// assert.Contains(stringOrSlice, substringOrElement [, message [, format-args]]) +// +// assert.NotContains(stringOrSlice, substringOrElement [, message [, format-args]]) +// +// assert.Panics(func(){ +// +// // call code that should panic +// +// } [, message [, format-args]]) +// +// assert.NotPanics(func(){ +// +// // call code that should not panic +// +// } [, message [, format-args]]) +// +// assert.WithinDuration(timeA, timeB, deltaTime, [, message [, format-args]]) +// +// assert.InDelta(numA, numB, delta, [, message [, format-args]]) +// +// assert.InEpsilon(numA, numB, epsilon, [, message [, format-args]]) +package assert diff --git a/vendor/github.com/stretchr/testify/assert/errors.go b/vendor/github.com/stretchr/testify/assert/errors.go new file mode 100644 index 0000000..ac9dc9d --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/errors.go @@ -0,0 +1,10 @@ +package assert + +import ( + "errors" +) + +// AnError is an error instance useful for testing. If the code does not care +// about error specifics, and only needs to return the error for example, this +// error should be used to make the test code more readable. +var AnError = errors.New("assert.AnError general error for testing") diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions.go b/vendor/github.com/stretchr/testify/assert/forward_assertions.go new file mode 100644 index 0000000..d8d3f53 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/forward_assertions.go @@ -0,0 +1,265 @@ +package assert + +import "time" + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + return Fail(a.t, failureMessage, msgAndArgs...) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements((*MyInterface)(nil), new(MyObject), "MyObject") +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + return Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + return IsType(a.t, expectedType, object, msgAndArgs...) +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Equal(expected, actual interface{}, msgAndArgs ...interface{}) bool { + return Equal(a.t, expected, actual, msgAndArgs...) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualValues(expected, actual interface{}, msgAndArgs ...interface{}) bool { + return EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// Exactly asserts that two objects are equal is value and type. +// +// assert.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Exactly(expected, actual interface{}, msgAndArgs ...interface{}) bool { + return Exactly(a.t, expected, actual, msgAndArgs...) +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + return NotNil(a.t, object, msgAndArgs...) +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + return Nil(a.t, object, msgAndArgs...) +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or a +// slice with len == 0. +// +// assert.Empty(obj) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + return Empty(a.t, object, msgAndArgs...) +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or a +// slice with len == 0. +// +// if assert.NotEmpty(obj) { +// assert.Equal("two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + return NotEmpty(a.t, object, msgAndArgs...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(mySlice, 3, "The size of slice is not 3") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + return Len(a.t, object, length, msgAndArgs...) +} + +// True asserts that the specified value is true. +// +// assert.True(myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { + return True(a.t, value, msgAndArgs...) +} + +// False asserts that the specified value is true. +// +// assert.False(myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { + return False(a.t, value, msgAndArgs...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEqual(expected, actual interface{}, msgAndArgs ...interface{}) bool { + return NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// Contains asserts that the specified string contains the specified substring. +// +// assert.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Contains(s, contains interface{}, msgAndArgs ...interface{}) bool { + return Contains(a.t, s, contains, msgAndArgs...) +} + +// NotContains asserts that the specified string does NOT contain the specified substring. +// +// assert.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotContains(s, contains interface{}, msgAndArgs ...interface{}) bool { + return NotContains(a.t, s, contains, msgAndArgs...) +} + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { + return Condition(a.t, comp, msgAndArgs...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + return Panics(a.t, f, msgAndArgs...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + return NotPanics(a.t, f, msgAndArgs...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) WithinDuration(expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InDelta(expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + return InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InEpsilon(expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(err) { +// assert.Equal(actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NoError(theError error, msgAndArgs ...interface{}) bool { + return NoError(a.t, theError, msgAndArgs...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(err, "An error was expected") { +// assert.Equal(err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Error(theError error, msgAndArgs ...interface{}) bool { + return Error(a.t, theError, msgAndArgs...) +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// if assert.Error(err, "An error was expected") { +// assert.Equal(err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + return EqualError(a.t, theError, errString, msgAndArgs...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + return Regexp(a.t, rx, str, msgAndArgs...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + return NotRegexp(a.t, rx, str, msgAndArgs...) +} diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go b/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go new file mode 100644 index 0000000..3df3f39 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/forward_assertions_test.go @@ -0,0 +1,511 @@ +package assert + +import ( + "errors" + "regexp" + "testing" + "time" +) + +func TestImplementsWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { + t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") + } + if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { + t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") + } +} + +func TestIsTypeWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { + t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") + } + if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { + t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") + } + +} + +func TestEqualWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Equal("Hello World", "Hello World") { + t.Error("Equal should return true") + } + if !assert.Equal(123, 123) { + t.Error("Equal should return true") + } + if !assert.Equal(123.5, 123.5) { + t.Error("Equal should return true") + } + if !assert.Equal([]byte("Hello World"), []byte("Hello World")) { + t.Error("Equal should return true") + } + if !assert.Equal(nil, nil) { + t.Error("Equal should return true") + } +} + +func TestEqualValuesWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.EqualValues(uint32(10), int32(10)) { + t.Error("EqualValues should return true") + } +} + +func TestNotNilWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.NotNil(new(AssertionTesterConformingObject)) { + t.Error("NotNil should return true: object is not nil") + } + if assert.NotNil(nil) { + t.Error("NotNil should return false: object is nil") + } + +} + +func TestNilWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.Nil(nil) { + t.Error("Nil should return true: object is nil") + } + if assert.Nil(new(AssertionTesterConformingObject)) { + t.Error("Nil should return false: object is not nil") + } + +} + +func TestTrueWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.True(true) { + t.Error("True should return true") + } + if assert.True(false) { + t.Error("True should return false") + } + +} + +func TestFalseWrapper(t *testing.T) { + assert := New(new(testing.T)) + + if !assert.False(false) { + t.Error("False should return true") + } + if assert.False(true) { + t.Error("False should return false") + } + +} + +func TestExactlyWrapper(t *testing.T) { + assert := New(new(testing.T)) + + a := float32(1) + b := float64(1) + c := float32(1) + d := float32(2) + + if assert.Exactly(a, b) { + t.Error("Exactly should return false") + } + if assert.Exactly(a, d) { + t.Error("Exactly should return false") + } + if !assert.Exactly(a, c) { + t.Error("Exactly should return true") + } + + if assert.Exactly(nil, a) { + t.Error("Exactly should return false") + } + if assert.Exactly(a, nil) { + t.Error("Exactly should return false") + } + +} + +func TestNotEqualWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotEqual("Hello World", "Hello World!") { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(123, 1234) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(123.5, 123.55) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) { + t.Error("NotEqual should return true") + } + if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) { + t.Error("NotEqual should return true") + } +} + +func TestContainsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + list := []string{"Foo", "Bar"} + + if !assert.Contains("Hello World", "Hello") { + t.Error("Contains should return true: \"Hello World\" contains \"Hello\"") + } + if assert.Contains("Hello World", "Salut") { + t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"") + } + + if !assert.Contains(list, "Foo") { + t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + if assert.Contains(list, "Salut") { + t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"") + } + +} + +func TestNotContainsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + list := []string{"Foo", "Bar"} + + if !assert.NotContains("Hello World", "Hello!") { + t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"") + } + if assert.NotContains("Hello World", "Hello") { + t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"") + } + + if !assert.NotContains(list, "Foo!") { + t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"") + } + if assert.NotContains(list, "Foo") { + t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"") + } + +} + +func TestConditionWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.Condition(func() bool { return true }, "Truth") { + t.Error("Condition should return true") + } + + if assert.Condition(func() bool { return false }, "Lie") { + t.Error("Condition should return false") + } + +} + +func TestDidPanicWrapper(t *testing.T) { + + if funcDidPanic, _ := didPanic(func() { + panic("Panic!") + }); !funcDidPanic { + t.Error("didPanic should return true") + } + + if funcDidPanic, _ := didPanic(func() { + }); funcDidPanic { + t.Error("didPanic should return false") + } + +} + +func TestPanicsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.Panics(func() { + panic("Panic!") + }) { + t.Error("Panics should return true") + } + + if assert.Panics(func() { + }) { + t.Error("Panics should return false") + } + +} + +func TestNotPanicsWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + if !assert.NotPanics(func() { + }) { + t.Error("NotPanics should return true") + } + + if assert.NotPanics(func() { + panic("Panic!") + }) { + t.Error("NotPanics should return false") + } + +} + +func TestNoErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + + assert.True(mockAssert.NoError(err), "NoError should return True for nil arg") + + // now set an error + err = errors.New("Some error") + + assert.False(mockAssert.NoError(err), "NoError with error should return False") + +} + +func TestErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + + assert.False(mockAssert.Error(err), "Error should return False for nil arg") + + // now set an error + err = errors.New("Some error") + + assert.True(mockAssert.Error(err), "Error with error should return True") + +} + +func TestEqualErrorWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + assert.False(mockAssert.EqualError(err, ""), + "EqualError should return false for nil arg") + + // now set an error + err = errors.New("some error") + assert.False(mockAssert.EqualError(err, "Not some error"), + "EqualError should return false for different error string") + assert.True(mockAssert.EqualError(err, "some error"), + "EqualError should return true") +} + +func TestEmptyWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.True(mockAssert.Empty(""), "Empty string is empty") + assert.True(mockAssert.Empty(nil), "Nil is empty") + assert.True(mockAssert.Empty([]string{}), "Empty string array is empty") + assert.True(mockAssert.Empty(0), "Zero int value is empty") + assert.True(mockAssert.Empty(false), "False value is empty") + + assert.False(mockAssert.Empty("something"), "Non Empty string is not empty") + assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty") + assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty") + assert.False(mockAssert.Empty(1), "Non-zero int value is not empty") + assert.False(mockAssert.Empty(true), "True value is not empty") + +} + +func TestNotEmptyWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.False(mockAssert.NotEmpty(""), "Empty string is empty") + assert.False(mockAssert.NotEmpty(nil), "Nil is empty") + assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty") + assert.False(mockAssert.NotEmpty(0), "Zero int value is empty") + assert.False(mockAssert.NotEmpty(false), "False value is empty") + + assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty") + assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty") + assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty") + assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty") + assert.True(mockAssert.NotEmpty(true), "True value is not empty") + +} + +func TestLenWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.False(mockAssert.Len(nil, 0), "nil does not have length") + assert.False(mockAssert.Len(0, 0), "int does not have length") + assert.False(mockAssert.Len(true, 0), "true does not have length") + assert.False(mockAssert.Len(false, 0), "false does not have length") + assert.False(mockAssert.Len('A', 0), "Rune does not have length") + assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length") + + ch := make(chan int, 5) + ch <- 1 + ch <- 2 + ch <- 3 + + cases := []struct { + v interface{} + l int + }{ + {[]int{1, 2, 3}, 3}, + {[...]int{1, 2, 3}, 3}, + {"ABC", 3}, + {map[int]int{1: 2, 2: 4, 3: 6}, 3}, + {ch, 3}, + + {[]int{}, 0}, + {map[int]int{}, 0}, + {make(chan int), 0}, + + {[]int(nil), 0}, + {map[int]int(nil), 0}, + {(chan int)(nil), 0}, + } + + for _, c := range cases { + assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l) + } +} + +func TestWithinDurationWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + a := time.Now() + b := a.Add(10 * time.Second) + + assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference") + assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference") + + assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference") + + assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference") + + assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference") + assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference") +} + +func TestInDeltaWrapper(t *testing.T) { + assert := New(new(testing.T)) + + True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01") + True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01") + True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1") + False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail") + False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail") + False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail") + + cases := []struct { + a, b interface{} + delta float64 + }{ + {uint8(2), uint8(1), 1}, + {uint16(2), uint16(1), 1}, + {uint32(2), uint32(1), 1}, + {uint64(2), uint64(1), 1}, + + {int(2), int(1), 1}, + {int8(2), int8(1), 1}, + {int16(2), int16(1), 1}, + {int32(2), int32(1), 1}, + {int64(2), int64(1), 1}, + + {float32(2), float32(1), 1}, + {float64(2), float64(1), 1}, + } + + for _, tc := range cases { + True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta) + } +} + +func TestInEpsilonWrapper(t *testing.T) { + assert := New(new(testing.T)) + + cases := []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), uint16(2), .001}, + {2.1, 2.2, 0.1}, + {2.2, 2.1, 0.1}, + {-2.1, -2.2, 0.1}, + {-2.2, -2.1, 0.1}, + {uint64(100), uint8(101), 0.01}, + {0.1, -0.1, 2}, + } + + for _, tc := range cases { + True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } + + cases = []struct { + a, b interface{} + epsilon float64 + }{ + {uint8(2), int16(-2), .001}, + {uint64(100), uint8(102), 0.01}, + {2.1, 2.2, 0.001}, + {2.2, 2.1, 0.001}, + {2.1, -2.2, 1}, + {2.1, "bla-bla", 0}, + {0.1, -0.1, 1.99}, + } + + for _, tc := range cases { + False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon)) + } +} + +func TestRegexpWrapper(t *testing.T) { + + assert := New(new(testing.T)) + + cases := []struct { + rx, str string + }{ + {"^start", "start of the line"}, + {"end$", "in the end"}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"}, + } + + for _, tc := range cases { + True(t, assert.Regexp(tc.rx, tc.str)) + True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) + False(t, assert.NotRegexp(tc.rx, tc.str)) + False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) + } + + cases = []struct { + rx, str string + }{ + {"^asdfastart", "Not the start of the line"}, + {"end$", "in the end."}, + {"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"}, + } + + for _, tc := range cases { + False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str) + False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str)) + True(t, assert.NotRegexp(tc.rx, tc.str)) + True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str)) + } +} diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go new file mode 100644 index 0000000..437a86c --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -0,0 +1,157 @@ +package assert + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strings" +) + +// httpCode is a helper that returns HTTP code of the response. It returns -1 +// if building a new request fails. +func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if err != nil { + return -1 + } + handler(w, req) + return w.Code +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { + code := httpCode(handler, method, url, values) + if code == -1 { + return false + } + return code >= http.StatusOK && code <= http.StatusPartialContent +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { + code := httpCode(handler, method, url, values) + if code == -1 { + return false + } + return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { + code := httpCode(handler, method, url, values) + if code == -1 { + return false + } + return code >= http.StatusBadRequest +} + +// HTTPBody is a helper that returns HTTP body of the response. It returns +// empty string if building a new request fails. +func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if err != nil { + return "" + } + handler(w, req) + return w.Body.String() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if !contains { + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + } + + return contains +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if contains { + Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body) + } + + return !contains +} + +// +// Assertions Wrappers +// + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method, url string, values url.Values) bool { + return HTTPSuccess(a.t, handler, method, url, values) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method, url string, values url.Values) bool { + return HTTPRedirect(a.t, handler, method, url, values) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method, url string, values url.Values) bool { + return HTTPError(a.t, handler, method, url, values) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { + return HTTPBodyContains(a.t, handler, method, url, values, str) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { + return HTTPBodyNotContains(a.t, handler, method, url, values, str) +} diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions_test.go b/vendor/github.com/stretchr/testify/assert/http_assertions_test.go new file mode 100644 index 0000000..684c2d5 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/http_assertions_test.go @@ -0,0 +1,86 @@ +package assert + +import ( + "fmt" + "net/http" + "net/url" + "testing" +) + +func httpOK(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func httpRedirect(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusTemporaryRedirect) +} + +func httpError(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusInternalServerError) +} + +func TestHTTPStatuses(t *testing.T) { + assert := New(t) + mockT := new(testing.T) + + assert.Equal(HTTPSuccess(mockT, httpOK, "GET", "/", nil), true) + assert.Equal(HTTPSuccess(mockT, httpRedirect, "GET", "/", nil), false) + assert.Equal(HTTPSuccess(mockT, httpError, "GET", "/", nil), false) + + assert.Equal(HTTPRedirect(mockT, httpOK, "GET", "/", nil), false) + assert.Equal(HTTPRedirect(mockT, httpRedirect, "GET", "/", nil), true) + assert.Equal(HTTPRedirect(mockT, httpError, "GET", "/", nil), false) + + assert.Equal(HTTPError(mockT, httpOK, "GET", "/", nil), false) + assert.Equal(HTTPError(mockT, httpRedirect, "GET", "/", nil), false) + assert.Equal(HTTPError(mockT, httpError, "GET", "/", nil), true) +} + +func TestHTTPStatusesWrapper(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true) + assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false) + + assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true) + assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false) + + assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false) + assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true) +} + +func httpHelloName(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + w.Write([]byte(fmt.Sprintf("Hello, %s!", name))) +} + +func TestHttpBody(t *testing.T) { + assert := New(t) + mockT := new(testing.T) + + assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + + assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) +} + +func TestHttpBodyWrappers(t *testing.T) { + assert := New(t) + mockAssert := New(new(testing.T)) + + assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + + assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!")) + assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World")) + assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world")) + +} diff --git a/vendor/golang.org/x/crypto/ssh/benchmark_test.go b/vendor/golang.org/x/crypto/ssh/benchmark_test.go new file mode 100644 index 0000000..d9f7eb9 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/benchmark_test.go @@ -0,0 +1,122 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "errors" + "io" + "net" + "testing" +) + +type server struct { + *ServerConn + chans <-chan NewChannel +} + +func newServer(c net.Conn, conf *ServerConfig) (*server, error) { + sconn, chans, reqs, err := NewServerConn(c, conf) + if err != nil { + return nil, err + } + go DiscardRequests(reqs) + return &server{sconn, chans}, nil +} + +func (s *server) Accept() (NewChannel, error) { + n, ok := <-s.chans + if !ok { + return nil, io.EOF + } + return n, nil +} + +func sshPipe() (Conn, *server, error) { + c1, c2, err := netPipe() + if err != nil { + return nil, nil, err + } + + clientConf := ClientConfig{ + User: "user", + } + serverConf := ServerConfig{ + NoClientAuth: true, + } + serverConf.AddHostKey(testSigners["ecdsa"]) + done := make(chan *server, 1) + go func() { + server, err := newServer(c2, &serverConf) + if err != nil { + done <- nil + } + done <- server + }() + + client, _, reqs, err := NewClientConn(c1, "", &clientConf) + if err != nil { + return nil, nil, err + } + + server := <-done + if server == nil { + return nil, nil, errors.New("server handshake failed.") + } + go DiscardRequests(reqs) + + return client, server, nil +} + +func BenchmarkEndToEnd(b *testing.B) { + b.StopTimer() + + client, server, err := sshPipe() + if err != nil { + b.Fatalf("sshPipe: %v", err) + } + + defer client.Close() + defer server.Close() + + size := (1 << 20) + input := make([]byte, size) + output := make([]byte, size) + b.SetBytes(int64(size)) + done := make(chan int, 1) + + go func() { + newCh, err := server.Accept() + if err != nil { + b.Fatalf("Client: %v", err) + } + ch, incoming, err := newCh.Accept() + go DiscardRequests(incoming) + for i := 0; i < b.N; i++ { + if _, err := io.ReadFull(ch, output); err != nil { + b.Fatalf("ReadFull: %v", err) + } + } + ch.Close() + done <- 1 + }() + + ch, in, err := client.OpenChannel("speed", nil) + if err != nil { + b.Fatalf("OpenChannel: %v", err) + } + go DiscardRequests(in) + + b.ResetTimer() + b.StartTimer() + for i := 0; i < b.N; i++ { + if _, err := ch.Write(input); err != nil { + b.Fatalf("WriteFull: %v", err) + } + } + ch.Close() + b.StopTimer() + + <-done +} diff --git a/vendor/golang.org/x/crypto/ssh/buffer.go b/vendor/golang.org/x/crypto/ssh/buffer.go new file mode 100644 index 0000000..6931b51 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/buffer.go @@ -0,0 +1,98 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "io" + "sync" +) + +// buffer provides a linked list buffer for data exchange +// between producer and consumer. Theoretically the buffer is +// of unlimited capacity as it does no allocation of its own. +type buffer struct { + // protects concurrent access to head, tail and closed + *sync.Cond + + head *element // the buffer that will be read first + tail *element // the buffer that will be read last + + closed bool +} + +// An element represents a single link in a linked list. +type element struct { + buf []byte + next *element +} + +// newBuffer returns an empty buffer that is not closed. +func newBuffer() *buffer { + e := new(element) + b := &buffer{ + Cond: newCond(), + head: e, + tail: e, + } + return b +} + +// write makes buf available for Read to receive. +// buf must not be modified after the call to write. +func (b *buffer) write(buf []byte) { + b.Cond.L.Lock() + e := &element{buf: buf} + b.tail.next = e + b.tail = e + b.Cond.Signal() + b.Cond.L.Unlock() +} + +// eof closes the buffer. Reads from the buffer once all +// the data has been consumed will receive os.EOF. +func (b *buffer) eof() error { + b.Cond.L.Lock() + b.closed = true + b.Cond.Signal() + b.Cond.L.Unlock() + return nil +} + +// Read reads data from the internal buffer in buf. Reads will block +// if no data is available, or until the buffer is closed. +func (b *buffer) Read(buf []byte) (n int, err error) { + b.Cond.L.Lock() + defer b.Cond.L.Unlock() + + for len(buf) > 0 { + // if there is data in b.head, copy it + if len(b.head.buf) > 0 { + r := copy(buf, b.head.buf) + buf, b.head.buf = buf[r:], b.head.buf[r:] + n += r + continue + } + // if there is a next buffer, make it the head + if len(b.head.buf) == 0 && b.head != b.tail { + b.head = b.head.next + continue + } + + // if at least one byte has been copied, return + if n > 0 { + break + } + + // if nothing was read, and there is nothing outstanding + // check to see if the buffer is closed. + if b.closed { + err = io.EOF + break + } + // out of buffers, wait for producer + b.Cond.Wait() + } + return +} diff --git a/vendor/golang.org/x/crypto/ssh/buffer_test.go b/vendor/golang.org/x/crypto/ssh/buffer_test.go new file mode 100644 index 0000000..d5781cb --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/buffer_test.go @@ -0,0 +1,87 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "io" + "testing" +) + +var alphabet = []byte("abcdefghijklmnopqrstuvwxyz") + +func TestBufferReadwrite(t *testing.T) { + b := newBuffer() + b.write(alphabet[:10]) + r, _ := b.Read(make([]byte, 10)) + if r != 10 { + t.Fatalf("Expected written == read == 10, written: 10, read %d", r) + } + + b = newBuffer() + b.write(alphabet[:5]) + r, _ = b.Read(make([]byte, 10)) + if r != 5 { + t.Fatalf("Expected written == read == 5, written: 5, read %d", r) + } + + b = newBuffer() + b.write(alphabet[:10]) + r, _ = b.Read(make([]byte, 5)) + if r != 5 { + t.Fatalf("Expected written == 10, read == 5, written: 10, read %d", r) + } + + b = newBuffer() + b.write(alphabet[:5]) + b.write(alphabet[5:15]) + r, _ = b.Read(make([]byte, 10)) + r2, _ := b.Read(make([]byte, 10)) + if r != 10 || r2 != 5 || 15 != r+r2 { + t.Fatal("Expected written == read == 15") + } +} + +func TestBufferClose(t *testing.T) { + b := newBuffer() + b.write(alphabet[:10]) + b.eof() + _, err := b.Read(make([]byte, 5)) + if err != nil { + t.Fatal("expected read of 5 to not return EOF") + } + b = newBuffer() + b.write(alphabet[:10]) + b.eof() + r, err := b.Read(make([]byte, 5)) + r2, err2 := b.Read(make([]byte, 10)) + if r != 5 || r2 != 5 || err != nil || err2 != nil { + t.Fatal("expected reads of 5 and 5") + } + + b = newBuffer() + b.write(alphabet[:10]) + b.eof() + r, err = b.Read(make([]byte, 5)) + r2, err2 = b.Read(make([]byte, 10)) + r3, err3 := b.Read(make([]byte, 10)) + if r != 5 || r2 != 5 || r3 != 0 || err != nil || err2 != nil || err3 != io.EOF { + t.Fatal("expected reads of 5 and 5 and 0, with EOF") + } + + b = newBuffer() + b.write(make([]byte, 5)) + b.write(make([]byte, 10)) + b.eof() + r, err = b.Read(make([]byte, 9)) + r2, err2 = b.Read(make([]byte, 3)) + r3, err3 = b.Read(make([]byte, 3)) + r4, err4 := b.Read(make([]byte, 10)) + if err != nil || err2 != nil || err3 != nil || err4 != io.EOF { + t.Fatalf("Expected EOF on forth read only, err=%v, err2=%v, err3=%v, err4=%v", err, err2, err3, err4) + } + if r != 9 || r2 != 3 || r3 != 3 || r4 != 0 { + t.Fatal("Expected written == read == 15", r, r2, r3, r4) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go new file mode 100644 index 0000000..3857700 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -0,0 +1,501 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" + "sort" + "time" +) + +// These constants from [PROTOCOL.certkeys] represent the algorithm names +// for certificate types supported by this package. +const ( + CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" + CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" + CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" + CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" + CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" +) + +// Certificate types distinguish between host and user +// certificates. The values can be set in the CertType field of +// Certificate. +const ( + UserCert = 1 + HostCert = 2 +) + +// Signature represents a cryptographic signature. +type Signature struct { + Format string + Blob []byte +} + +// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that +// a certificate does not expire. +const CertTimeInfinity = 1<<64 - 1 + +// An Certificate represents an OpenSSH certificate as defined in +// [PROTOCOL.certkeys]?rev=1.8. +type Certificate struct { + Nonce []byte + Key PublicKey + Serial uint64 + CertType uint32 + KeyId string + ValidPrincipals []string + ValidAfter uint64 + ValidBefore uint64 + Permissions + Reserved []byte + SignatureKey PublicKey + Signature *Signature +} + +// genericCertData holds the key-independent part of the certificate data. +// Overall, certificates contain an nonce, public key fields and +// key-independent fields. +type genericCertData struct { + Serial uint64 + CertType uint32 + KeyId string + ValidPrincipals []byte + ValidAfter uint64 + ValidBefore uint64 + CriticalOptions []byte + Extensions []byte + Reserved []byte + SignatureKey []byte + Signature []byte +} + +func marshalStringList(namelist []string) []byte { + var to []byte + for _, name := range namelist { + s := struct{ N string }{name} + to = append(to, Marshal(&s)...) + } + return to +} + +type optionsTuple struct { + Key string + Value []byte +} + +type optionsTupleValue struct { + Value string +} + +// serialize a map of critical options or extensions +// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, +// we need two length prefixes for a non-empty string value +func marshalTuples(tups map[string]string) []byte { + keys := make([]string, 0, len(tups)) + for key := range tups { + keys = append(keys, key) + } + sort.Strings(keys) + + var ret []byte + for _, key := range keys { + s := optionsTuple{Key: key} + if value := tups[key]; len(value) > 0 { + s.Value = Marshal(&optionsTupleValue{value}) + } + ret = append(ret, Marshal(&s)...) + } + return ret +} + +// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, +// we need two length prefixes for a non-empty option value +func parseTuples(in []byte) (map[string]string, error) { + tups := map[string]string{} + var lastKey string + var haveLastKey bool + + for len(in) > 0 { + var key, val, extra []byte + var ok bool + + if key, in, ok = parseString(in); !ok { + return nil, errShortRead + } + keyStr := string(key) + // according to [PROTOCOL.certkeys], the names must be in + // lexical order. + if haveLastKey && keyStr <= lastKey { + return nil, fmt.Errorf("ssh: certificate options are not in lexical order") + } + lastKey, haveLastKey = keyStr, true + // the next field is a data field, which if non-empty has a string embedded + if val, in, ok = parseString(in); !ok { + return nil, errShortRead + } + if len(val) > 0 { + val, extra, ok = parseString(val) + if !ok { + return nil, errShortRead + } + if len(extra) > 0 { + return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") + } + tups[keyStr] = string(val) + } else { + tups[keyStr] = "" + } + } + return tups, nil +} + +func parseCert(in []byte, privAlgo string) (*Certificate, error) { + nonce, rest, ok := parseString(in) + if !ok { + return nil, errShortRead + } + + key, rest, err := parsePubKey(rest, privAlgo) + if err != nil { + return nil, err + } + + var g genericCertData + if err := Unmarshal(rest, &g); err != nil { + return nil, err + } + + c := &Certificate{ + Nonce: nonce, + Key: key, + Serial: g.Serial, + CertType: g.CertType, + KeyId: g.KeyId, + ValidAfter: g.ValidAfter, + ValidBefore: g.ValidBefore, + } + + for principals := g.ValidPrincipals; len(principals) > 0; { + principal, rest, ok := parseString(principals) + if !ok { + return nil, errShortRead + } + c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) + principals = rest + } + + c.CriticalOptions, err = parseTuples(g.CriticalOptions) + if err != nil { + return nil, err + } + c.Extensions, err = parseTuples(g.Extensions) + if err != nil { + return nil, err + } + c.Reserved = g.Reserved + k, err := ParsePublicKey(g.SignatureKey) + if err != nil { + return nil, err + } + + c.SignatureKey = k + c.Signature, rest, ok = parseSignatureBody(g.Signature) + if !ok || len(rest) > 0 { + return nil, errors.New("ssh: signature parse error") + } + + return c, nil +} + +type openSSHCertSigner struct { + pub *Certificate + signer Signer +} + +// NewCertSigner returns a Signer that signs with the given Certificate, whose +// private key is held by signer. It returns an error if the public key in cert +// doesn't match the key used by signer. +func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { + if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { + return nil, errors.New("ssh: signer and cert have different public key") + } + + return &openSSHCertSigner{cert, signer}, nil +} + +func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { + return s.signer.Sign(rand, data) +} + +func (s *openSSHCertSigner) PublicKey() PublicKey { + return s.pub +} + +const sourceAddressCriticalOption = "source-address" + +// CertChecker does the work of verifying a certificate. Its methods +// can be plugged into ClientConfig.HostKeyCallback and +// ServerConfig.PublicKeyCallback. For the CertChecker to work, +// minimally, the IsAuthority callback should be set. +type CertChecker struct { + // SupportedCriticalOptions lists the CriticalOptions that the + // server application layer understands. These are only used + // for user certificates. + SupportedCriticalOptions []string + + // IsAuthority should return true if the key is recognized as + // an authority. This allows for certificates to be signed by other + // certificates. + IsAuthority func(auth PublicKey) bool + + // Clock is used for verifying time stamps. If nil, time.Now + // is used. + Clock func() time.Time + + // UserKeyFallback is called when CertChecker.Authenticate encounters a + // public key that is not a certificate. It must implement validation + // of user keys or else, if nil, all such keys are rejected. + UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) + + // HostKeyFallback is called when CertChecker.CheckHostKey encounters a + // public key that is not a certificate. It must implement host key + // validation or else, if nil, all such keys are rejected. + HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error + + // IsRevoked is called for each certificate so that revocation checking + // can be implemented. It should return true if the given certificate + // is revoked and false otherwise. If nil, no certificates are + // considered to have been revoked. + IsRevoked func(cert *Certificate) bool +} + +// CheckHostKey checks a host key certificate. This method can be +// plugged into ClientConfig.HostKeyCallback. +func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { + cert, ok := key.(*Certificate) + if !ok { + if c.HostKeyFallback != nil { + return c.HostKeyFallback(addr, remote, key) + } + return errors.New("ssh: non-certificate host key") + } + if cert.CertType != HostCert { + return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) + } + + return c.CheckCert(addr, cert) +} + +// Authenticate checks a user certificate. Authenticate can be used as +// a value for ServerConfig.PublicKeyCallback. +func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { + cert, ok := pubKey.(*Certificate) + if !ok { + if c.UserKeyFallback != nil { + return c.UserKeyFallback(conn, pubKey) + } + return nil, errors.New("ssh: normal key pairs not accepted") + } + + if cert.CertType != UserCert { + return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) + } + + if err := c.CheckCert(conn.User(), cert); err != nil { + return nil, err + } + + return &cert.Permissions, nil +} + +// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and +// the signature of the certificate. +func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { + if c.IsRevoked != nil && c.IsRevoked(cert) { + return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) + } + + for opt, _ := range cert.CriticalOptions { + // sourceAddressCriticalOption will be enforced by + // serverAuthenticate + if opt == sourceAddressCriticalOption { + continue + } + + found := false + for _, supp := range c.SupportedCriticalOptions { + if supp == opt { + found = true + break + } + } + if !found { + return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) + } + } + + if len(cert.ValidPrincipals) > 0 { + // By default, certs are valid for all users/hosts. + found := false + for _, p := range cert.ValidPrincipals { + if p == principal { + found = true + break + } + } + if !found { + return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) + } + } + + if !c.IsAuthority(cert.SignatureKey) { + return fmt.Errorf("ssh: certificate signed by unrecognized authority") + } + + clock := c.Clock + if clock == nil { + clock = time.Now + } + + unixNow := clock().Unix() + if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { + return fmt.Errorf("ssh: cert is not yet valid") + } + if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { + return fmt.Errorf("ssh: cert has expired") + } + if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { + return fmt.Errorf("ssh: certificate signature does not verify") + } + + return nil +} + +// SignCert sets c.SignatureKey to the authority's public key and stores a +// Signature, by authority, in the certificate. +func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { + c.Nonce = make([]byte, 32) + if _, err := io.ReadFull(rand, c.Nonce); err != nil { + return err + } + c.SignatureKey = authority.PublicKey() + + sig, err := authority.Sign(rand, c.bytesForSigning()) + if err != nil { + return err + } + c.Signature = sig + return nil +} + +var certAlgoNames = map[string]string{ + KeyAlgoRSA: CertAlgoRSAv01, + KeyAlgoDSA: CertAlgoDSAv01, + KeyAlgoECDSA256: CertAlgoECDSA256v01, + KeyAlgoECDSA384: CertAlgoECDSA384v01, + KeyAlgoECDSA521: CertAlgoECDSA521v01, +} + +// certToPrivAlgo returns the underlying algorithm for a certificate algorithm. +// Panics if a non-certificate algorithm is passed. +func certToPrivAlgo(algo string) string { + for privAlgo, pubAlgo := range certAlgoNames { + if pubAlgo == algo { + return privAlgo + } + } + panic("unknown cert algorithm") +} + +func (cert *Certificate) bytesForSigning() []byte { + c2 := *cert + c2.Signature = nil + out := c2.Marshal() + // Drop trailing signature length. + return out[:len(out)-4] +} + +// Marshal serializes c into OpenSSH's wire format. It is part of the +// PublicKey interface. +func (c *Certificate) Marshal() []byte { + generic := genericCertData{ + Serial: c.Serial, + CertType: c.CertType, + KeyId: c.KeyId, + ValidPrincipals: marshalStringList(c.ValidPrincipals), + ValidAfter: uint64(c.ValidAfter), + ValidBefore: uint64(c.ValidBefore), + CriticalOptions: marshalTuples(c.CriticalOptions), + Extensions: marshalTuples(c.Extensions), + Reserved: c.Reserved, + SignatureKey: c.SignatureKey.Marshal(), + } + if c.Signature != nil { + generic.Signature = Marshal(c.Signature) + } + genericBytes := Marshal(&generic) + keyBytes := c.Key.Marshal() + _, keyBytes, _ = parseString(keyBytes) + prefix := Marshal(&struct { + Name string + Nonce []byte + Key []byte `ssh:"rest"` + }{c.Type(), c.Nonce, keyBytes}) + + result := make([]byte, 0, len(prefix)+len(genericBytes)) + result = append(result, prefix...) + result = append(result, genericBytes...) + return result +} + +// Type returns the key name. It is part of the PublicKey interface. +func (c *Certificate) Type() string { + algo, ok := certAlgoNames[c.Key.Type()] + if !ok { + panic("unknown cert key type") + } + return algo +} + +// Verify verifies a signature against the certificate's public +// key. It is part of the PublicKey interface. +func (c *Certificate) Verify(data []byte, sig *Signature) error { + return c.Key.Verify(data, sig) +} + +func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { + format, in, ok := parseString(in) + if !ok { + return + } + + out = &Signature{ + Format: string(format), + } + + if out.Blob, in, ok = parseString(in); !ok { + return + } + + return out, in, ok +} + +func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { + sigBytes, rest, ok := parseString(in) + if !ok { + return + } + + out, trailing, ok := parseSignatureBody(sigBytes) + if !ok || len(trailing) > 0 { + return nil, nil, false + } + return +} diff --git a/vendor/golang.org/x/crypto/ssh/certs_test.go b/vendor/golang.org/x/crypto/ssh/certs_test.go new file mode 100644 index 0000000..c5f2e53 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/certs_test.go @@ -0,0 +1,216 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto/rand" + "reflect" + "testing" + "time" +) + +// Cert generated by ssh-keygen 6.0p1 Debian-4. +// % ssh-keygen -s ca-key -I test user-key +const exampleSSHCert = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb1srW/W3ZDjYAO45xLYAwzHBDLsJ4Ux6ICFIkTjb1LEAAAADAQABAAAAYQCkoR51poH0wE8w72cqSB8Sszx+vAhzcMdCO0wqHTj7UNENHWEXGrU0E0UQekD7U+yhkhtoyjbPOVIP7hNa6aRk/ezdh/iUnCIt4Jt1v3Z1h1P+hA4QuYFMHNB+rmjPwAcAAAAAAAAAAAAAAAEAAAAEdGVzdAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAHcAAAAHc3NoLXJzYQAAAAMBAAEAAABhANFS2kaktpSGc+CcmEKPyw9mJC4nZKxHKTgLVZeaGbFZOvJTNzBspQHdy7Q1uKSfktxpgjZnksiu/tFF9ngyY2KFoc+U88ya95IZUycBGCUbBQ8+bhDtw/icdDGQD5WnUwAAAG8AAAAHc3NoLXJzYQAAAGC8Y9Z2LQKhIhxf52773XaWrXdxP0t3GBVo4A10vUWiYoAGepr6rQIoGGXFxT4B9Gp+nEBJjOwKDXPrAevow0T9ca8gZN+0ykbhSrXLE5Ao48rqr3zP4O1/9P7e6gp0gw8=` + +func TestParseCert(t *testing.T) { + authKeyBytes := []byte(exampleSSHCert) + + key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes) + if err != nil { + t.Fatalf("ParseAuthorizedKey: %v", err) + } + if len(rest) > 0 { + t.Errorf("rest: got %q, want empty", rest) + } + + if _, ok := key.(*Certificate); !ok { + t.Fatalf("got %v (%T), want *Certificate", key, key) + } + + marshaled := MarshalAuthorizedKey(key) + // Before comparison, remove the trailing newline that + // MarshalAuthorizedKey adds. + marshaled = marshaled[:len(marshaled)-1] + if !bytes.Equal(authKeyBytes, marshaled) { + t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes) + } +} + +// Cert generated by ssh-keygen OpenSSH_6.8p1 OS X 10.10.3 +// % ssh-keygen -s ca -I testcert -O source-address=192.168.1.0/24 -O force-command=/bin/sleep user.pub +// user.pub key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMN +// Critical Options: +// force-command /bin/sleep +// source-address 192.168.1.0/24 +// Extensions: +// permit-X11-forwarding +// permit-agent-forwarding +// permit-port-forwarding +// permit-pty +// permit-user-rc +const exampleSSHCertWithOptions = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgDyysCJY0XrO1n03EeRRoITnTPdjENFmWDs9X58PP3VUAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMNAAAAAAAAAAAAAAABAAAACHRlc3RjZXJ0AAAAAAAAAAAAAAAA//////////8AAABLAAAADWZvcmNlLWNvbW1hbmQAAAAOAAAACi9iaW4vc2xlZXAAAAAOc291cmNlLWFkZHJlc3MAAAASAAAADjE5Mi4xNjguMS4wLzI0AAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAwU+c5ui5A8+J/CFpjW8wCa52bEODA808WWQDCSuTG/eMXNf59v9Y8Pk0F1E9dGCosSNyVcB/hacUrc6He+i97+HJCyKavBsE6GDxrjRyxYqAlfcOXi/IVmaUGiO8OQ39d4GHrjToInKvExSUeleQyH4Y4/e27T/pILAqPFL3fyrvMLT5qU9QyIt6zIpa7GBP5+urouNavMprV3zsfIqNBbWypinOQAw823a5wN+zwXnhZrgQiHZ/USG09Y6k98y1dTVz8YHlQVR4D3lpTAsKDKJ5hCH9WU4fdf+lU8OyNGaJ/vz0XNqxcToe1l4numLTnaoSuH89pHryjqurB7lJKwAAAQ8AAAAHc3NoLXJzYQAAAQCaHvUIoPL1zWUHIXLvu96/HU1s/i4CAW2IIEuGgxCUCiFj6vyTyYtgxQxcmbfZf6eaITlS6XJZa7Qq4iaFZh75C1DXTX8labXhRSD4E2t//AIP9MC1rtQC5xo6FmbQ+BoKcDskr+mNACcbRSxs3IL3bwCfWDnIw2WbVox9ZdcthJKk4UoCW4ix4QwdHw7zlddlz++fGEEVhmTbll1SUkycGApPFBsAYRTMupUJcYPIeReBI/m8XfkoMk99bV8ZJQTAd7OekHY2/48Ff53jLmyDjP7kNw1F8OaPtkFs6dGJXta4krmaekPy87j+35In5hFj7yoOqvSbmYUkeX70/GGQ` + +func TestParseCertWithOptions(t *testing.T) { + opts := map[string]string{ + "source-address": "192.168.1.0/24", + "force-command": "/bin/sleep", + } + exts := map[string]string{ + "permit-X11-forwarding": "", + "permit-agent-forwarding": "", + "permit-port-forwarding": "", + "permit-pty": "", + "permit-user-rc": "", + } + authKeyBytes := []byte(exampleSSHCertWithOptions) + + key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes) + if err != nil { + t.Fatalf("ParseAuthorizedKey: %v", err) + } + if len(rest) > 0 { + t.Errorf("rest: got %q, want empty", rest) + } + cert, ok := key.(*Certificate) + if !ok { + t.Fatalf("got %v (%T), want *Certificate", key, key) + } + if !reflect.DeepEqual(cert.CriticalOptions, opts) { + t.Errorf("unexpected critical options - got %v, want %v", cert.CriticalOptions, opts) + } + if !reflect.DeepEqual(cert.Extensions, exts) { + t.Errorf("unexpected Extensions - got %v, want %v", cert.Extensions, exts) + } + marshaled := MarshalAuthorizedKey(key) + // Before comparison, remove the trailing newline that + // MarshalAuthorizedKey adds. + marshaled = marshaled[:len(marshaled)-1] + if !bytes.Equal(authKeyBytes, marshaled) { + t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes) + } +} + +func TestValidateCert(t *testing.T) { + key, _, _, _, err := ParseAuthorizedKey([]byte(exampleSSHCert)) + if err != nil { + t.Fatalf("ParseAuthorizedKey: %v", err) + } + validCert, ok := key.(*Certificate) + if !ok { + t.Fatalf("got %v (%T), want *Certificate", key, key) + } + checker := CertChecker{} + checker.IsAuthority = func(k PublicKey) bool { + return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal()) + } + + if err := checker.CheckCert("user", validCert); err != nil { + t.Errorf("Unable to validate certificate: %v", err) + } + invalidCert := &Certificate{ + Key: testPublicKeys["rsa"], + SignatureKey: testPublicKeys["ecdsa"], + ValidBefore: CertTimeInfinity, + Signature: &Signature{}, + } + if err := checker.CheckCert("user", invalidCert); err == nil { + t.Error("Invalid cert signature passed validation") + } +} + +func TestValidateCertTime(t *testing.T) { + cert := Certificate{ + ValidPrincipals: []string{"user"}, + Key: testPublicKeys["rsa"], + ValidAfter: 50, + ValidBefore: 100, + } + + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + + for ts, ok := range map[int64]bool{ + 25: false, + 50: true, + 99: true, + 100: false, + 125: false, + } { + checker := CertChecker{ + Clock: func() time.Time { return time.Unix(ts, 0) }, + } + checker.IsAuthority = func(k PublicKey) bool { + return bytes.Equal(k.Marshal(), + testPublicKeys["ecdsa"].Marshal()) + } + + if v := checker.CheckCert("user", &cert); (v == nil) != ok { + t.Errorf("Authenticate(%d): %v", ts, v) + } + } +} + +// TODO(hanwen): tests for +// +// host keys: +// * fallbacks + +func TestHostKeyCert(t *testing.T) { + cert := &Certificate{ + ValidPrincipals: []string{"hostname", "hostname.domain"}, + Key: testPublicKeys["rsa"], + ValidBefore: CertTimeInfinity, + CertType: HostCert, + } + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + + checker := &CertChecker{ + IsAuthority: func(p PublicKey) bool { + return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal()) + }, + } + + certSigner, err := NewCertSigner(cert, testSigners["rsa"]) + if err != nil { + t.Errorf("NewCertSigner: %v", err) + } + + for _, name := range []string{"hostname", "otherhost"} { + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + errc := make(chan error) + + go func() { + conf := ServerConfig{ + NoClientAuth: true, + } + conf.AddHostKey(certSigner) + _, _, _, err := NewServerConn(c1, &conf) + errc <- err + }() + + config := &ClientConfig{ + User: "user", + HostKeyCallback: checker.CheckHostKey, + } + _, _, _, err = NewClientConn(c2, name, config) + + succeed := name == "hostname" + if (err == nil) != succeed { + t.Fatalf("NewClientConn(%q): %v", name, err) + } + + err = <-errc + if (err == nil) != succeed { + t.Fatalf("NewServerConn(%q): %v", name, err) + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go new file mode 100644 index 0000000..5403c7e --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -0,0 +1,631 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "log" + "sync" +) + +const ( + minPacketLength = 9 + // channelMaxPacket contains the maximum number of bytes that will be + // sent in a single packet. As per RFC 4253, section 6.1, 32k is also + // the minimum. + channelMaxPacket = 1 << 15 + // We follow OpenSSH here. + channelWindowSize = 64 * channelMaxPacket +) + +// NewChannel represents an incoming request to a channel. It must either be +// accepted for use by calling Accept, or rejected by calling Reject. +type NewChannel interface { + // Accept accepts the channel creation request. It returns the Channel + // and a Go channel containing SSH requests. The Go channel must be + // serviced otherwise the Channel will hang. + Accept() (Channel, <-chan *Request, error) + + // Reject rejects the channel creation request. After calling + // this, no other methods on the Channel may be called. + Reject(reason RejectionReason, message string) error + + // ChannelType returns the type of the channel, as supplied by the + // client. + ChannelType() string + + // ExtraData returns the arbitrary payload for this channel, as supplied + // by the client. This data is specific to the channel type. + ExtraData() []byte +} + +// A Channel is an ordered, reliable, flow-controlled, duplex stream +// that is multiplexed over an SSH connection. +type Channel interface { + // Read reads up to len(data) bytes from the channel. + Read(data []byte) (int, error) + + // Write writes len(data) bytes to the channel. + Write(data []byte) (int, error) + + // Close signals end of channel use. No data may be sent after this + // call. + Close() error + + // CloseWrite signals the end of sending in-band + // data. Requests may still be sent, and the other side may + // still send data + CloseWrite() error + + // SendRequest sends a channel request. If wantReply is true, + // it will wait for a reply and return the result as a + // boolean, otherwise the return value will be false. Channel + // requests are out-of-band messages so they may be sent even + // if the data stream is closed or blocked by flow control. + SendRequest(name string, wantReply bool, payload []byte) (bool, error) + + // Stderr returns an io.ReadWriter that writes to this channel + // with the extended data type set to stderr. Stderr may + // safely be read and written from a different goroutine than + // Read and Write respectively. + Stderr() io.ReadWriter +} + +// Request is a request sent outside of the normal stream of +// data. Requests can either be specific to an SSH channel, or they +// can be global. +type Request struct { + Type string + WantReply bool + Payload []byte + + ch *channel + mux *mux +} + +// Reply sends a response to a request. It must be called for all requests +// where WantReply is true and is a no-op otherwise. The payload argument is +// ignored for replies to channel-specific requests. +func (r *Request) Reply(ok bool, payload []byte) error { + if !r.WantReply { + return nil + } + + if r.ch == nil { + return r.mux.ackRequest(ok, payload) + } + + return r.ch.ackRequest(ok) +} + +// RejectionReason is an enumeration used when rejecting channel creation +// requests. See RFC 4254, section 5.1. +type RejectionReason uint32 + +const ( + Prohibited RejectionReason = iota + 1 + ConnectionFailed + UnknownChannelType + ResourceShortage +) + +// String converts the rejection reason to human readable form. +func (r RejectionReason) String() string { + switch r { + case Prohibited: + return "administratively prohibited" + case ConnectionFailed: + return "connect failed" + case UnknownChannelType: + return "unknown channel type" + case ResourceShortage: + return "resource shortage" + } + return fmt.Sprintf("unknown reason %d", int(r)) +} + +func min(a uint32, b int) uint32 { + if a < uint32(b) { + return a + } + return uint32(b) +} + +type channelDirection uint8 + +const ( + channelInbound channelDirection = iota + channelOutbound +) + +// channel is an implementation of the Channel interface that works +// with the mux class. +type channel struct { + // R/O after creation + chanType string + extraData []byte + localId, remoteId uint32 + + // maxIncomingPayload and maxRemotePayload are the maximum + // payload sizes of normal and extended data packets for + // receiving and sending, respectively. The wire packet will + // be 9 or 13 bytes larger (excluding encryption overhead). + maxIncomingPayload uint32 + maxRemotePayload uint32 + + mux *mux + + // decided is set to true if an accept or reject message has been sent + // (for outbound channels) or received (for inbound channels). + decided bool + + // direction contains either channelOutbound, for channels created + // locally, or channelInbound, for channels created by the peer. + direction channelDirection + + // Pending internal channel messages. + msg chan interface{} + + // Since requests have no ID, there can be only one request + // with WantReply=true outstanding. This lock is held by a + // goroutine that has such an outgoing request pending. + sentRequestMu sync.Mutex + + incomingRequests chan *Request + + sentEOF bool + + // thread-safe data + remoteWin window + pending *buffer + extPending *buffer + + // windowMu protects myWindow, the flow-control window. + windowMu sync.Mutex + myWindow uint32 + + // writeMu serializes calls to mux.conn.writePacket() and + // protects sentClose and packetPool. This mutex must be + // different from windowMu, as writePacket can block if there + // is a key exchange pending. + writeMu sync.Mutex + sentClose bool + + // packetPool has a buffer for each extended channel ID to + // save allocations during writes. + packetPool map[uint32][]byte +} + +// writePacket sends a packet. If the packet is a channel close, it updates +// sentClose. This method takes the lock c.writeMu. +func (c *channel) writePacket(packet []byte) error { + c.writeMu.Lock() + if c.sentClose { + c.writeMu.Unlock() + return io.EOF + } + c.sentClose = (packet[0] == msgChannelClose) + err := c.mux.conn.writePacket(packet) + c.writeMu.Unlock() + return err +} + +func (c *channel) sendMessage(msg interface{}) error { + if debugMux { + log.Printf("send %d: %#v", c.mux.chanList.offset, msg) + } + + p := Marshal(msg) + binary.BigEndian.PutUint32(p[1:], c.remoteId) + return c.writePacket(p) +} + +// WriteExtended writes data to a specific extended stream. These streams are +// used, for example, for stderr. +func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) { + if c.sentEOF { + return 0, io.EOF + } + // 1 byte message type, 4 bytes remoteId, 4 bytes data length + opCode := byte(msgChannelData) + headerLength := uint32(9) + if extendedCode > 0 { + headerLength += 4 + opCode = msgChannelExtendedData + } + + c.writeMu.Lock() + packet := c.packetPool[extendedCode] + // We don't remove the buffer from packetPool, so + // WriteExtended calls from different goroutines will be + // flagged as errors by the race detector. + c.writeMu.Unlock() + + for len(data) > 0 { + space := min(c.maxRemotePayload, len(data)) + if space, err = c.remoteWin.reserve(space); err != nil { + return n, err + } + if want := headerLength + space; uint32(cap(packet)) < want { + packet = make([]byte, want) + } else { + packet = packet[:want] + } + + todo := data[:space] + + packet[0] = opCode + binary.BigEndian.PutUint32(packet[1:], c.remoteId) + if extendedCode > 0 { + binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode)) + } + binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo))) + copy(packet[headerLength:], todo) + if err = c.writePacket(packet); err != nil { + return n, err + } + + n += len(todo) + data = data[len(todo):] + } + + c.writeMu.Lock() + c.packetPool[extendedCode] = packet + c.writeMu.Unlock() + + return n, err +} + +func (c *channel) handleData(packet []byte) error { + headerLen := 9 + isExtendedData := packet[0] == msgChannelExtendedData + if isExtendedData { + headerLen = 13 + } + if len(packet) < headerLen { + // malformed data packet + return parseError(packet[0]) + } + + var extended uint32 + if isExtendedData { + extended = binary.BigEndian.Uint32(packet[5:]) + } + + length := binary.BigEndian.Uint32(packet[headerLen-4 : headerLen]) + if length == 0 { + return nil + } + if length > c.maxIncomingPayload { + // TODO(hanwen): should send Disconnect? + return errors.New("ssh: incoming packet exceeds maximum payload size") + } + + data := packet[headerLen:] + if length != uint32(len(data)) { + return errors.New("ssh: wrong packet length") + } + + c.windowMu.Lock() + if c.myWindow < length { + c.windowMu.Unlock() + // TODO(hanwen): should send Disconnect with reason? + return errors.New("ssh: remote side wrote too much") + } + c.myWindow -= length + c.windowMu.Unlock() + + if extended == 1 { + c.extPending.write(data) + } else if extended > 0 { + // discard other extended data. + } else { + c.pending.write(data) + } + return nil +} + +func (c *channel) adjustWindow(n uint32) error { + c.windowMu.Lock() + // Since myWindow is managed on our side, and can never exceed + // the initial window setting, we don't worry about overflow. + c.myWindow += uint32(n) + c.windowMu.Unlock() + return c.sendMessage(windowAdjustMsg{ + AdditionalBytes: uint32(n), + }) +} + +func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) { + switch extended { + case 1: + n, err = c.extPending.Read(data) + case 0: + n, err = c.pending.Read(data) + default: + return 0, fmt.Errorf("ssh: extended code %d unimplemented", extended) + } + + if n > 0 { + err = c.adjustWindow(uint32(n)) + // sendWindowAdjust can return io.EOF if the remote + // peer has closed the connection, however we want to + // defer forwarding io.EOF to the caller of Read until + // the buffer has been drained. + if n > 0 && err == io.EOF { + err = nil + } + } + + return n, err +} + +func (c *channel) close() { + c.pending.eof() + c.extPending.eof() + close(c.msg) + close(c.incomingRequests) + c.writeMu.Lock() + // This is not necesary for a normal channel teardown, but if + // there was another error, it is. + c.sentClose = true + c.writeMu.Unlock() + // Unblock writers. + c.remoteWin.close() +} + +// responseMessageReceived is called when a success or failure message is +// received on a channel to check that such a message is reasonable for the +// given channel. +func (c *channel) responseMessageReceived() error { + if c.direction == channelInbound { + return errors.New("ssh: channel response message received on inbound channel") + } + if c.decided { + return errors.New("ssh: duplicate response received for channel") + } + c.decided = true + return nil +} + +func (c *channel) handlePacket(packet []byte) error { + switch packet[0] { + case msgChannelData, msgChannelExtendedData: + return c.handleData(packet) + case msgChannelClose: + c.sendMessage(channelCloseMsg{PeersId: c.remoteId}) + c.mux.chanList.remove(c.localId) + c.close() + return nil + case msgChannelEOF: + // RFC 4254 is mute on how EOF affects dataExt messages but + // it is logical to signal EOF at the same time. + c.extPending.eof() + c.pending.eof() + return nil + } + + decoded, err := decode(packet) + if err != nil { + return err + } + + switch msg := decoded.(type) { + case *channelOpenFailureMsg: + if err := c.responseMessageReceived(); err != nil { + return err + } + c.mux.chanList.remove(msg.PeersId) + c.msg <- msg + case *channelOpenConfirmMsg: + if err := c.responseMessageReceived(); err != nil { + return err + } + if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { + return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize) + } + c.remoteId = msg.MyId + c.maxRemotePayload = msg.MaxPacketSize + c.remoteWin.add(msg.MyWindow) + c.msg <- msg + case *windowAdjustMsg: + if !c.remoteWin.add(msg.AdditionalBytes) { + return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes) + } + case *channelRequestMsg: + req := Request{ + Type: msg.Request, + WantReply: msg.WantReply, + Payload: msg.RequestSpecificData, + ch: c, + } + + c.incomingRequests <- &req + default: + c.msg <- msg + } + return nil +} + +func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel { + ch := &channel{ + remoteWin: window{Cond: newCond()}, + myWindow: channelWindowSize, + pending: newBuffer(), + extPending: newBuffer(), + direction: direction, + incomingRequests: make(chan *Request, 16), + msg: make(chan interface{}, 16), + chanType: chanType, + extraData: extraData, + mux: m, + packetPool: make(map[uint32][]byte), + } + ch.localId = m.chanList.add(ch) + return ch +} + +var errUndecided = errors.New("ssh: must Accept or Reject channel") +var errDecidedAlready = errors.New("ssh: can call Accept or Reject only once") + +type extChannel struct { + code uint32 + ch *channel +} + +func (e *extChannel) Write(data []byte) (n int, err error) { + return e.ch.WriteExtended(data, e.code) +} + +func (e *extChannel) Read(data []byte) (n int, err error) { + return e.ch.ReadExtended(data, e.code) +} + +func (c *channel) Accept() (Channel, <-chan *Request, error) { + if c.decided { + return nil, nil, errDecidedAlready + } + c.maxIncomingPayload = channelMaxPacket + confirm := channelOpenConfirmMsg{ + PeersId: c.remoteId, + MyId: c.localId, + MyWindow: c.myWindow, + MaxPacketSize: c.maxIncomingPayload, + } + c.decided = true + if err := c.sendMessage(confirm); err != nil { + return nil, nil, err + } + + return c, c.incomingRequests, nil +} + +func (ch *channel) Reject(reason RejectionReason, message string) error { + if ch.decided { + return errDecidedAlready + } + reject := channelOpenFailureMsg{ + PeersId: ch.remoteId, + Reason: reason, + Message: message, + Language: "en", + } + ch.decided = true + return ch.sendMessage(reject) +} + +func (ch *channel) Read(data []byte) (int, error) { + if !ch.decided { + return 0, errUndecided + } + return ch.ReadExtended(data, 0) +} + +func (ch *channel) Write(data []byte) (int, error) { + if !ch.decided { + return 0, errUndecided + } + return ch.WriteExtended(data, 0) +} + +func (ch *channel) CloseWrite() error { + if !ch.decided { + return errUndecided + } + ch.sentEOF = true + return ch.sendMessage(channelEOFMsg{ + PeersId: ch.remoteId}) +} + +func (ch *channel) Close() error { + if !ch.decided { + return errUndecided + } + + return ch.sendMessage(channelCloseMsg{ + PeersId: ch.remoteId}) +} + +// Extended returns an io.ReadWriter that sends and receives data on the given, +// SSH extended stream. Such streams are used, for example, for stderr. +func (ch *channel) Extended(code uint32) io.ReadWriter { + if !ch.decided { + return nil + } + return &extChannel{code, ch} +} + +func (ch *channel) Stderr() io.ReadWriter { + return ch.Extended(1) +} + +func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { + if !ch.decided { + return false, errUndecided + } + + if wantReply { + ch.sentRequestMu.Lock() + defer ch.sentRequestMu.Unlock() + } + + msg := channelRequestMsg{ + PeersId: ch.remoteId, + Request: name, + WantReply: wantReply, + RequestSpecificData: payload, + } + + if err := ch.sendMessage(msg); err != nil { + return false, err + } + + if wantReply { + m, ok := (<-ch.msg) + if !ok { + return false, io.EOF + } + switch m.(type) { + case *channelRequestFailureMsg: + return false, nil + case *channelRequestSuccessMsg: + return true, nil + default: + return false, fmt.Errorf("ssh: unexpected response to channel request: %#v", m) + } + } + + return false, nil +} + +// ackRequest either sends an ack or nack to the channel request. +func (ch *channel) ackRequest(ok bool) error { + if !ch.decided { + return errUndecided + } + + var msg interface{} + if !ok { + msg = channelRequestFailureMsg{ + PeersId: ch.remoteId, + } + } else { + msg = channelRequestSuccessMsg{ + PeersId: ch.remoteId, + } + } + return ch.sendMessage(msg) +} + +func (ch *channel) ChannelType() string { + return ch.chanType +} + +func (ch *channel) ExtraData() []byte { + return ch.extraData +} diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go new file mode 100644 index 0000000..3e06da0 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -0,0 +1,549 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rc4" + "crypto/subtle" + "encoding/binary" + "errors" + "fmt" + "hash" + "io" + "io/ioutil" +) + +const ( + packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. + + // RFC 4253 section 6.1 defines a minimum packet size of 32768 that implementations + // MUST be able to process (plus a few more kilobytes for padding and mac). The RFC + // indicates implementations SHOULD be able to handle larger packet sizes, but then + // waffles on about reasonable limits. + // + // OpenSSH caps their maxPacket at 256kB so we choose to do + // the same. maxPacket is also used to ensure that uint32 + // length fields do not overflow, so it should remain well + // below 4G. + maxPacket = 256 * 1024 +) + +// noneCipher implements cipher.Stream and provides no encryption. It is used +// by the transport before the first key-exchange. +type noneCipher struct{} + +func (c noneCipher) XORKeyStream(dst, src []byte) { + copy(dst, src) +} + +func newAESCTR(key, iv []byte) (cipher.Stream, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewCTR(c, iv), nil +} + +func newRC4(key, iv []byte) (cipher.Stream, error) { + return rc4.NewCipher(key) +} + +type streamCipherMode struct { + keySize int + ivSize int + skip int + createFunc func(key, iv []byte) (cipher.Stream, error) +} + +func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { + if len(key) < c.keySize { + panic("ssh: key length too small for cipher") + } + if len(iv) < c.ivSize { + panic("ssh: iv too small for cipher") + } + + stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize]) + if err != nil { + return nil, err + } + + var streamDump []byte + if c.skip > 0 { + streamDump = make([]byte, 512) + } + + for remainingToDump := c.skip; remainingToDump > 0; { + dumpThisTime := remainingToDump + if dumpThisTime > len(streamDump) { + dumpThisTime = len(streamDump) + } + stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) + remainingToDump -= dumpThisTime + } + + return stream, nil +} + +// cipherModes documents properties of supported ciphers. Ciphers not included +// are not supported and will not be negotiated, even if explicitly requested in +// ClientConfig.Crypto.Ciphers. +var cipherModes = map[string]*streamCipherMode{ + // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms + // are defined in the order specified in the RFC. + "aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, + "aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, + "aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, + + // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. + // They are defined in the order specified in the RFC. + "arcfour128": {16, 0, 1536, newRC4}, + "arcfour256": {32, 0, 1536, newRC4}, + + // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. + // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and + // RC4) has problems with weak keys, and should be used with caution." + // RFC4345 introduces improved versions of Arcfour. + "arcfour": {16, 0, 0, newRC4}, + + // AES-GCM is not a stream cipher, so it is constructed with a + // special case. If we add any more non-stream ciphers, we + // should invest a cleaner way to do this. + gcmCipherID: {16, 12, 0, nil}, + + // insecure cipher, see http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf + // uncomment below to enable it. + // aes128cbcID: {16, aes.BlockSize, 0, nil}, +} + +// prefixLen is the length of the packet prefix that contains the packet length +// and number of padding bytes. +const prefixLen = 5 + +// streamPacketCipher is a packetCipher using a stream cipher. +type streamPacketCipher struct { + mac hash.Hash + cipher cipher.Stream + + // The following members are to avoid per-packet allocations. + prefix [prefixLen]byte + seqNumBytes [4]byte + padding [2 * packetSizeMultiple]byte + packetData []byte + macResult []byte +} + +// readPacket reads and decrypt a single packet from the reader argument. +func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { + if _, err := io.ReadFull(r, s.prefix[:]); err != nil { + return nil, err + } + + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + length := binary.BigEndian.Uint32(s.prefix[0:4]) + paddingLength := uint32(s.prefix[4]) + + var macSize uint32 + if s.mac != nil { + s.mac.Reset() + binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) + s.mac.Write(s.seqNumBytes[:]) + s.mac.Write(s.prefix[:]) + macSize = uint32(s.mac.Size()) + } + + if length <= paddingLength+1 { + return nil, errors.New("ssh: invalid packet length, packet too small") + } + + if length > maxPacket { + return nil, errors.New("ssh: invalid packet length, packet too large") + } + + // the maxPacket check above ensures that length-1+macSize + // does not overflow. + if uint32(cap(s.packetData)) < length-1+macSize { + s.packetData = make([]byte, length-1+macSize) + } else { + s.packetData = s.packetData[:length-1+macSize] + } + + if _, err := io.ReadFull(r, s.packetData); err != nil { + return nil, err + } + mac := s.packetData[length-1:] + data := s.packetData[:length-1] + s.cipher.XORKeyStream(data, data) + + if s.mac != nil { + s.mac.Write(data) + s.macResult = s.mac.Sum(s.macResult[:0]) + if subtle.ConstantTimeCompare(s.macResult, mac) != 1 { + return nil, errors.New("ssh: MAC failure") + } + } + + return s.packetData[:length-paddingLength-1], nil +} + +// writePacket encrypts and sends a packet of data to the writer argument +func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + if len(packet) > maxPacket { + return errors.New("ssh: packet too large") + } + + paddingLength := packetSizeMultiple - (prefixLen+len(packet))%packetSizeMultiple + if paddingLength < 4 { + paddingLength += packetSizeMultiple + } + + length := len(packet) + 1 + paddingLength + binary.BigEndian.PutUint32(s.prefix[:], uint32(length)) + s.prefix[4] = byte(paddingLength) + padding := s.padding[:paddingLength] + if _, err := io.ReadFull(rand, padding); err != nil { + return err + } + + if s.mac != nil { + s.mac.Reset() + binary.BigEndian.PutUint32(s.seqNumBytes[:], seqNum) + s.mac.Write(s.seqNumBytes[:]) + s.mac.Write(s.prefix[:]) + s.mac.Write(packet) + s.mac.Write(padding) + } + + s.cipher.XORKeyStream(s.prefix[:], s.prefix[:]) + s.cipher.XORKeyStream(packet, packet) + s.cipher.XORKeyStream(padding, padding) + + if _, err := w.Write(s.prefix[:]); err != nil { + return err + } + if _, err := w.Write(packet); err != nil { + return err + } + if _, err := w.Write(padding); err != nil { + return err + } + + if s.mac != nil { + s.macResult = s.mac.Sum(s.macResult[:0]) + if _, err := w.Write(s.macResult); err != nil { + return err + } + } + + return nil +} + +type gcmCipher struct { + aead cipher.AEAD + prefix [4]byte + iv []byte + buf []byte +} + +func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aead, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + return &gcmCipher{ + aead: aead, + iv: iv, + }, nil +} + +const gcmTagSize = 16 + +func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + // Pad out to multiple of 16 bytes. This is different from the + // stream cipher because that encrypts the length too. + padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple) + if padding < 4 { + padding += packetSizeMultiple + } + + length := uint32(len(packet) + int(padding) + 1) + binary.BigEndian.PutUint32(c.prefix[:], length) + if _, err := w.Write(c.prefix[:]); err != nil { + return err + } + + if cap(c.buf) < int(length) { + c.buf = make([]byte, length) + } else { + c.buf = c.buf[:length] + } + + c.buf[0] = padding + copy(c.buf[1:], packet) + if _, err := io.ReadFull(rand, c.buf[1+len(packet):]); err != nil { + return err + } + c.buf = c.aead.Seal(c.buf[:0], c.iv, c.buf, c.prefix[:]) + if _, err := w.Write(c.buf); err != nil { + return err + } + c.incIV() + + return nil +} + +func (c *gcmCipher) incIV() { + for i := 4 + 7; i >= 4; i-- { + c.iv[i]++ + if c.iv[i] != 0 { + break + } + } +} + +func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { + if _, err := io.ReadFull(r, c.prefix[:]); err != nil { + return nil, err + } + length := binary.BigEndian.Uint32(c.prefix[:]) + if length > maxPacket { + return nil, errors.New("ssh: max packet length exceeded.") + } + + if cap(c.buf) < int(length+gcmTagSize) { + c.buf = make([]byte, length+gcmTagSize) + } else { + c.buf = c.buf[:length+gcmTagSize] + } + + if _, err := io.ReadFull(r, c.buf); err != nil { + return nil, err + } + + plain, err := c.aead.Open(c.buf[:0], c.iv, c.buf, c.prefix[:]) + if err != nil { + return nil, err + } + c.incIV() + + padding := plain[0] + if padding < 4 || padding >= 20 { + return nil, fmt.Errorf("ssh: illegal padding %d", padding) + } + + if int(padding+1) >= len(plain) { + return nil, fmt.Errorf("ssh: padding %d too large", padding) + } + plain = plain[1 : length-uint32(padding)] + return plain, nil +} + +// cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1 +type cbcCipher struct { + mac hash.Hash + macSize uint32 + decrypter cipher.BlockMode + encrypter cipher.BlockMode + + // The following members are to avoid per-packet allocations. + seqNumBytes [4]byte + packetData []byte + macResult []byte + + // Amount of data we should still read to hide which + // verification error triggered. + oracleCamouflage uint32 +} + +func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + cbc := &cbcCipher{ + mac: macModes[algs.MAC].new(macKey), + decrypter: cipher.NewCBCDecrypter(c, iv), + encrypter: cipher.NewCBCEncrypter(c, iv), + packetData: make([]byte, 1024), + } + if cbc.mac != nil { + cbc.macSize = uint32(cbc.mac.Size()) + } + + return cbc, nil +} + +func maxUInt32(a, b int) uint32 { + if a > b { + return uint32(a) + } + return uint32(b) +} + +const ( + cbcMinPacketSizeMultiple = 8 + cbcMinPacketSize = 16 + cbcMinPaddingSize = 4 +) + +// cbcError represents a verification error that may leak information. +type cbcError string + +func (e cbcError) Error() string { return string(e) } + +func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) { + p, err := c.readPacketLeaky(seqNum, r) + if err != nil { + if _, ok := err.(cbcError); ok { + // Verification error: read a fixed amount of + // data, to make distinguishing between + // failing MAC and failing length check more + // difficult. + io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage)) + } + } + return p, err +} + +func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) { + blockSize := c.decrypter.BlockSize() + + // Read the header, which will include some of the subsequent data in the + // case of block ciphers - this is copied back to the payload later. + // How many bytes of payload/padding will be read with this first read. + firstBlockLength := uint32((prefixLen + blockSize - 1) / blockSize * blockSize) + firstBlock := c.packetData[:firstBlockLength] + if _, err := io.ReadFull(r, firstBlock); err != nil { + return nil, err + } + + c.oracleCamouflage = maxPacket + 4 + c.macSize - firstBlockLength + + c.decrypter.CryptBlocks(firstBlock, firstBlock) + length := binary.BigEndian.Uint32(firstBlock[:4]) + if length > maxPacket { + return nil, cbcError("ssh: packet too large") + } + if length+4 < maxUInt32(cbcMinPacketSize, blockSize) { + // The minimum size of a packet is 16 (or the cipher block size, whichever + // is larger) bytes. + return nil, cbcError("ssh: packet too small") + } + // The length of the packet (including the length field but not the MAC) must + // be a multiple of the block size or 8, whichever is larger. + if (length+4)%maxUInt32(cbcMinPacketSizeMultiple, blockSize) != 0 { + return nil, cbcError("ssh: invalid packet length multiple") + } + + paddingLength := uint32(firstBlock[4]) + if paddingLength < cbcMinPaddingSize || length <= paddingLength+1 { + return nil, cbcError("ssh: invalid packet length") + } + + // Positions within the c.packetData buffer: + macStart := 4 + length + paddingStart := macStart - paddingLength + + // Entire packet size, starting before length, ending at end of mac. + entirePacketSize := macStart + c.macSize + + // Ensure c.packetData is large enough for the entire packet data. + if uint32(cap(c.packetData)) < entirePacketSize { + // Still need to upsize and copy, but this should be rare at runtime, only + // on upsizing the packetData buffer. + c.packetData = make([]byte, entirePacketSize) + copy(c.packetData, firstBlock) + } else { + c.packetData = c.packetData[:entirePacketSize] + } + + if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil { + return nil, err + } else { + c.oracleCamouflage -= uint32(n) + } + + remainingCrypted := c.packetData[firstBlockLength:macStart] + c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) + + mac := c.packetData[macStart:] + if c.mac != nil { + c.mac.Reset() + binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) + c.mac.Write(c.seqNumBytes[:]) + c.mac.Write(c.packetData[:macStart]) + c.macResult = c.mac.Sum(c.macResult[:0]) + if subtle.ConstantTimeCompare(c.macResult, mac) != 1 { + return nil, cbcError("ssh: MAC failure") + } + } + + return c.packetData[prefixLen:paddingStart], nil +} + +func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error { + effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize()) + + // Length of encrypted portion of the packet (header, payload, padding). + // Enforce minimum padding and packet size. + encLength := maxUInt32(prefixLen+len(packet)+cbcMinPaddingSize, cbcMinPaddingSize) + // Enforce block size. + encLength = (encLength + effectiveBlockSize - 1) / effectiveBlockSize * effectiveBlockSize + + length := encLength - 4 + paddingLength := int(length) - (1 + len(packet)) + + // Overall buffer contains: header, payload, padding, mac. + // Space for the MAC is reserved in the capacity but not the slice length. + bufferSize := encLength + c.macSize + if uint32(cap(c.packetData)) < bufferSize { + c.packetData = make([]byte, encLength, bufferSize) + } else { + c.packetData = c.packetData[:encLength] + } + + p := c.packetData + + // Packet header. + binary.BigEndian.PutUint32(p, length) + p = p[4:] + p[0] = byte(paddingLength) + + // Payload. + p = p[1:] + copy(p, packet) + + // Padding. + p = p[len(packet):] + if _, err := io.ReadFull(rand, p); err != nil { + return err + } + + if c.mac != nil { + c.mac.Reset() + binary.BigEndian.PutUint32(c.seqNumBytes[:], seqNum) + c.mac.Write(c.seqNumBytes[:]) + c.mac.Write(c.packetData) + // The MAC is now appended into the capacity reserved for it earlier. + c.packetData = c.mac.Sum(c.packetData) + } + + c.encrypter.CryptBlocks(c.packetData[:encLength], c.packetData[:encLength]) + + if _, err := w.Write(c.packetData); err != nil { + return err + } + + return nil +} diff --git a/vendor/golang.org/x/crypto/ssh/cipher_test.go b/vendor/golang.org/x/crypto/ssh/cipher_test.go new file mode 100644 index 0000000..54b92b6 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/cipher_test.go @@ -0,0 +1,127 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/rand" + "testing" +) + +func TestDefaultCiphersExist(t *testing.T) { + for _, cipherAlgo := range supportedCiphers { + if _, ok := cipherModes[cipherAlgo]; !ok { + t.Errorf("default cipher %q is unknown", cipherAlgo) + } + } +} + +func TestPacketCiphers(t *testing.T) { + // Still test aes128cbc cipher althought it's commented out. + cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil} + defer delete(cipherModes, aes128cbcID) + + for cipher := range cipherModes { + kr := &kexResult{Hash: crypto.SHA1} + algs := directionAlgorithms{ + Cipher: cipher, + MAC: "hmac-sha1", + Compression: "none", + } + client, err := newPacketCipher(clientKeys, algs, kr) + if err != nil { + t.Errorf("newPacketCipher(client, %q): %v", cipher, err) + continue + } + server, err := newPacketCipher(clientKeys, algs, kr) + if err != nil { + t.Errorf("newPacketCipher(client, %q): %v", cipher, err) + continue + } + + want := "bla bla" + input := []byte(want) + buf := &bytes.Buffer{} + if err := client.writePacket(0, buf, rand.Reader, input); err != nil { + t.Errorf("writePacket(%q): %v", cipher, err) + continue + } + + packet, err := server.readPacket(0, buf) + if err != nil { + t.Errorf("readPacket(%q): %v", cipher, err) + continue + } + + if string(packet) != want { + t.Errorf("roundtrip(%q): got %q, want %q", cipher, packet, want) + } + } +} + +func TestCBCOracleCounterMeasure(t *testing.T) { + cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil} + defer delete(cipherModes, aes128cbcID) + + kr := &kexResult{Hash: crypto.SHA1} + algs := directionAlgorithms{ + Cipher: aes128cbcID, + MAC: "hmac-sha1", + Compression: "none", + } + client, err := newPacketCipher(clientKeys, algs, kr) + if err != nil { + t.Fatalf("newPacketCipher(client): %v", err) + } + + want := "bla bla" + input := []byte(want) + buf := &bytes.Buffer{} + if err := client.writePacket(0, buf, rand.Reader, input); err != nil { + t.Errorf("writePacket: %v", err) + } + + packetSize := buf.Len() + buf.Write(make([]byte, 2*maxPacket)) + + // We corrupt each byte, but this usually will only test the + // 'packet too large' or 'MAC failure' cases. + lastRead := -1 + for i := 0; i < packetSize; i++ { + server, err := newPacketCipher(clientKeys, algs, kr) + if err != nil { + t.Fatalf("newPacketCipher(client): %v", err) + } + + fresh := &bytes.Buffer{} + fresh.Write(buf.Bytes()) + fresh.Bytes()[i] ^= 0x01 + + before := fresh.Len() + _, err = server.readPacket(0, fresh) + if err == nil { + t.Errorf("corrupt byte %d: readPacket succeeded ", i) + continue + } + if _, ok := err.(cbcError); !ok { + t.Errorf("corrupt byte %d: got %v (%T), want cbcError", i, err, err) + continue + } + + after := fresh.Len() + bytesRead := before - after + if bytesRead < maxPacket { + t.Errorf("corrupt byte %d: read %d bytes, want more than %d", i, bytesRead, maxPacket) + continue + } + + if i > 0 && bytesRead != lastRead { + t.Errorf("corrupt byte %d: read %d bytes, want %d bytes read", i, bytesRead, lastRead) + } + lastRead = bytesRead + } +} diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go new file mode 100644 index 0000000..0b9fbe5 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -0,0 +1,213 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "errors" + "fmt" + "net" + "sync" +) + +// Client implements a traditional SSH client that supports shells, +// subprocesses, port forwarding and tunneled dialing. +type Client struct { + Conn + + forwards forwardList // forwarded tcpip connections from the remote side + mu sync.Mutex + channelHandlers map[string]chan NewChannel +} + +// HandleChannelOpen returns a channel on which NewChannel requests +// for the given type are sent. If the type already is being handled, +// nil is returned. The channel is closed when the connection is closed. +func (c *Client) HandleChannelOpen(channelType string) <-chan NewChannel { + c.mu.Lock() + defer c.mu.Unlock() + if c.channelHandlers == nil { + // The SSH channel has been closed. + c := make(chan NewChannel) + close(c) + return c + } + + ch := c.channelHandlers[channelType] + if ch != nil { + return nil + } + + ch = make(chan NewChannel, 16) + c.channelHandlers[channelType] = ch + return ch +} + +// NewClient creates a Client on top of the given connection. +func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client { + conn := &Client{ + Conn: c, + channelHandlers: make(map[string]chan NewChannel, 1), + } + + go conn.handleGlobalRequests(reqs) + go conn.handleChannelOpens(chans) + go func() { + conn.Wait() + conn.forwards.closeAll() + }() + go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip")) + return conn +} + +// NewClientConn establishes an authenticated SSH connection using c +// as the underlying transport. The Request and NewChannel channels +// must be serviced or the connection will hang. +func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) { + fullConf := *config + fullConf.SetDefaults() + conn := &connection{ + sshConn: sshConn{conn: c}, + } + + if err := conn.clientHandshake(addr, &fullConf); err != nil { + c.Close() + return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) + } + conn.mux = newMux(conn.transport) + return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil +} + +// clientHandshake performs the client side key exchange. See RFC 4253 Section +// 7. +func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { + if config.ClientVersion != "" { + c.clientVersion = []byte(config.ClientVersion) + } else { + c.clientVersion = []byte(packageVersion) + } + var err error + c.serverVersion, err = exchangeVersions(c.sshConn.conn, c.clientVersion) + if err != nil { + return err + } + + c.transport = newClientTransport( + newTransport(c.sshConn.conn, config.Rand, true /* is client */), + c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) + if err := c.transport.requestKeyChange(); err != nil { + return err + } + + if packet, err := c.transport.readPacket(); err != nil { + return err + } else if packet[0] != msgNewKeys { + return unexpectedMessageError(msgNewKeys, packet[0]) + } + + // We just did the key change, so the session ID is established. + c.sessionID = c.transport.getSessionID() + + return c.clientAuthenticate(config) +} + +// verifyHostKeySignature verifies the host key obtained in the key +// exchange. +func verifyHostKeySignature(hostKey PublicKey, result *kexResult) error { + sig, rest, ok := parseSignatureBody(result.Signature) + if len(rest) > 0 || !ok { + return errors.New("ssh: signature parse error") + } + + return hostKey.Verify(result.H, sig) +} + +// NewSession opens a new Session for this client. (A session is a remote +// execution of a program.) +func (c *Client) NewSession() (*Session, error) { + ch, in, err := c.OpenChannel("session", nil) + if err != nil { + return nil, err + } + return newSession(ch, in) +} + +func (c *Client) handleGlobalRequests(incoming <-chan *Request) { + for r := range incoming { + // This handles keepalive messages and matches + // the behaviour of OpenSSH. + r.Reply(false, nil) + } +} + +// handleChannelOpens channel open messages from the remote side. +func (c *Client) handleChannelOpens(in <-chan NewChannel) { + for ch := range in { + c.mu.Lock() + handler := c.channelHandlers[ch.ChannelType()] + c.mu.Unlock() + + if handler != nil { + handler <- ch + } else { + ch.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", ch.ChannelType())) + } + } + + c.mu.Lock() + for _, ch := range c.channelHandlers { + close(ch) + } + c.channelHandlers = nil + c.mu.Unlock() +} + +// Dial starts a client connection to the given SSH server. It is a +// convenience function that connects to the given network address, +// initiates the SSH handshake, and then sets up a Client. For access +// to incoming channels and requests, use net.Dial with NewClientConn +// instead. +func Dial(network, addr string, config *ClientConfig) (*Client, error) { + conn, err := net.Dial(network, addr) + if err != nil { + return nil, err + } + c, chans, reqs, err := NewClientConn(conn, addr, config) + if err != nil { + return nil, err + } + return NewClient(c, chans, reqs), nil +} + +// A ClientConfig structure is used to configure a Client. It must not be +// modified after having been passed to an SSH function. +type ClientConfig struct { + // Config contains configuration that is shared between clients and + // servers. + Config + + // User contains the username to authenticate as. + User string + + // Auth contains possible authentication methods to use with the + // server. Only the first instance of a particular RFC 4252 method will + // be used during authentication. + Auth []AuthMethod + + // HostKeyCallback, if not nil, is called during the cryptographic + // handshake to validate the server's host key. A nil HostKeyCallback + // implies that all host keys are accepted. + HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + + // ClientVersion contains the version identification string that will + // be used for the connection. If empty, a reasonable default is used. + ClientVersion string + + // HostKeyAlgorithms lists the key types that the client will + // accept from the server as host key, in order of + // preference. If empty, a reasonable default is used. Any + // string returned from PublicKey.Type method may be used, or + // any of the CertAlgoXxxx and KeyAlgoXxxx constants. + HostKeyAlgorithms []string +} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go new file mode 100644 index 0000000..e15be3e --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -0,0 +1,441 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" +) + +// clientAuthenticate authenticates with the remote server. See RFC 4252. +func (c *connection) clientAuthenticate(config *ClientConfig) error { + // initiate user auth session + if err := c.transport.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth})); err != nil { + return err + } + packet, err := c.transport.readPacket() + if err != nil { + return err + } + var serviceAccept serviceAcceptMsg + if err := Unmarshal(packet, &serviceAccept); err != nil { + return err + } + + // during the authentication phase the client first attempts the "none" method + // then any untried methods suggested by the server. + tried := make(map[string]bool) + var lastMethods []string + for auth := AuthMethod(new(noneAuth)); auth != nil; { + ok, methods, err := auth.auth(c.transport.getSessionID(), config.User, c.transport, config.Rand) + if err != nil { + return err + } + if ok { + // success + return nil + } + tried[auth.method()] = true + if methods == nil { + methods = lastMethods + } + lastMethods = methods + + auth = nil + + findNext: + for _, a := range config.Auth { + candidateMethod := a.method() + if tried[candidateMethod] { + continue + } + for _, meth := range methods { + if meth == candidateMethod { + auth = a + break findNext + } + } + } + } + return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", keys(tried)) +} + +func keys(m map[string]bool) []string { + s := make([]string, 0, len(m)) + + for key := range m { + s = append(s, key) + } + return s +} + +// An AuthMethod represents an instance of an RFC 4252 authentication method. +type AuthMethod interface { + // auth authenticates user over transport t. + // Returns true if authentication is successful. + // If authentication is not successful, a []string of alternative + // method names is returned. If the slice is nil, it will be ignored + // and the previous set of possible methods will be reused. + auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error) + + // method returns the RFC 4252 method name. + method() string +} + +// "none" authentication, RFC 4252 section 5.2. +type noneAuth int + +func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + if err := c.writePacket(Marshal(&userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: "none", + })); err != nil { + return false, nil, err + } + + return handleAuthResponse(c) +} + +func (n *noneAuth) method() string { + return "none" +} + +// passwordCallback is an AuthMethod that fetches the password through +// a function call, e.g. by prompting the user. +type passwordCallback func() (password string, err error) + +func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + type passwordAuthMsg struct { + User string `sshtype:"50"` + Service string + Method string + Reply bool + Password string + } + + pw, err := cb() + // REVIEW NOTE: is there a need to support skipping a password attempt? + // The program may only find out that the user doesn't have a password + // when prompting. + if err != nil { + return false, nil, err + } + + if err := c.writePacket(Marshal(&passwordAuthMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + Reply: false, + Password: pw, + })); err != nil { + return false, nil, err + } + + return handleAuthResponse(c) +} + +func (cb passwordCallback) method() string { + return "password" +} + +// Password returns an AuthMethod using the given password. +func Password(secret string) AuthMethod { + return passwordCallback(func() (string, error) { return secret, nil }) +} + +// PasswordCallback returns an AuthMethod that uses a callback for +// fetching a password. +func PasswordCallback(prompt func() (secret string, err error)) AuthMethod { + return passwordCallback(prompt) +} + +type publickeyAuthMsg struct { + User string `sshtype:"50"` + Service string + Method string + // HasSig indicates to the receiver packet that the auth request is signed and + // should be used for authentication of the request. + HasSig bool + Algoname string + PubKey []byte + // Sig is tagged with "rest" so Marshal will exclude it during + // validateKey + Sig []byte `ssh:"rest"` +} + +// publicKeyCallback is an AuthMethod that uses a set of key +// pairs for authentication. +type publicKeyCallback func() ([]Signer, error) + +func (cb publicKeyCallback) method() string { + return "publickey" +} + +func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + // Authentication is performed in two stages. The first stage sends an + // enquiry to test if each key is acceptable to the remote. The second + // stage attempts to authenticate with the valid keys obtained in the + // first stage. + + signers, err := cb() + if err != nil { + return false, nil, err + } + var validKeys []Signer + for _, signer := range signers { + if ok, err := validateKey(signer.PublicKey(), user, c); ok { + validKeys = append(validKeys, signer) + } else { + if err != nil { + return false, nil, err + } + } + } + + // methods that may continue if this auth is not successful. + var methods []string + for _, signer := range validKeys { + pub := signer.PublicKey() + + pubKey := pub.Marshal() + sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + }, []byte(pub.Type()), pubKey)) + if err != nil { + return false, nil, err + } + + // manually wrap the serialized signature in a string + s := Marshal(sign) + sig := make([]byte, stringLength(len(s))) + marshalString(sig, s) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: cb.method(), + HasSig: true, + Algoname: pub.Type(), + PubKey: pubKey, + Sig: sig, + } + p := Marshal(&msg) + if err := c.writePacket(p); err != nil { + return false, nil, err + } + var success bool + success, methods, err = handleAuthResponse(c) + if err != nil { + return false, nil, err + } + if success { + return success, methods, err + } + } + return false, methods, nil +} + +// validateKey validates the key provided is acceptable to the server. +func validateKey(key PublicKey, user string, c packetConn) (bool, error) { + pubKey := key.Marshal() + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: "publickey", + HasSig: false, + Algoname: key.Type(), + PubKey: pubKey, + } + if err := c.writePacket(Marshal(&msg)); err != nil { + return false, err + } + + return confirmKeyAck(key, c) +} + +func confirmKeyAck(key PublicKey, c packetConn) (bool, error) { + pubKey := key.Marshal() + algoname := key.Type() + + for { + packet, err := c.readPacket() + if err != nil { + return false, err + } + switch packet[0] { + case msgUserAuthBanner: + // TODO(gpaul): add callback to present the banner to the user + case msgUserAuthPubKeyOk: + var msg userAuthPubKeyOkMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, err + } + if msg.Algo != algoname || !bytes.Equal(msg.PubKey, pubKey) { + return false, nil + } + return true, nil + case msgUserAuthFailure: + return false, nil + default: + return false, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + } +} + +// PublicKeys returns an AuthMethod that uses the given key +// pairs. +func PublicKeys(signers ...Signer) AuthMethod { + return publicKeyCallback(func() ([]Signer, error) { return signers, nil }) +} + +// PublicKeysCallback returns an AuthMethod that runs the given +// function to obtain a list of key pairs. +func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod { + return publicKeyCallback(getSigners) +} + +// handleAuthResponse returns whether the preceding authentication request succeeded +// along with a list of remaining authentication methods to try next and +// an error if an unexpected response was received. +func handleAuthResponse(c packetConn) (bool, []string, error) { + for { + packet, err := c.readPacket() + if err != nil { + return false, nil, err + } + + switch packet[0] { + case msgUserAuthBanner: + // TODO: add callback to present the banner to the user + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, nil, err + } + return false, msg.Methods, nil + case msgUserAuthSuccess: + return true, nil, nil + case msgDisconnect: + return false, nil, io.EOF + default: + return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) + } + } +} + +// KeyboardInteractiveChallenge should print questions, optionally +// disabling echoing (e.g. for passwords), and return all the answers. +// Challenge may be called multiple times in a single session. After +// successful authentication, the server may send a challenge with no +// questions, for which the user and instruction messages should be +// printed. RFC 4256 section 3.3 details how the UI should behave for +// both CLI and GUI environments. +type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) + +// KeyboardInteractive returns a AuthMethod using a prompt/response +// sequence controlled by the server. +func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { + return challenge +} + +func (cb KeyboardInteractiveChallenge) method() string { + return "keyboard-interactive" +} + +func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { + type initiateMsg struct { + User string `sshtype:"50"` + Service string + Method string + Language string + Submethods string + } + + if err := c.writePacket(Marshal(&initiateMsg{ + User: user, + Service: serviceSSH, + Method: "keyboard-interactive", + })); err != nil { + return false, nil, err + } + + for { + packet, err := c.readPacket() + if err != nil { + return false, nil, err + } + + // like handleAuthResponse, but with less options. + switch packet[0] { + case msgUserAuthBanner: + // TODO: Print banners during userauth. + continue + case msgUserAuthInfoRequest: + // OK + case msgUserAuthFailure: + var msg userAuthFailureMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, nil, err + } + return false, msg.Methods, nil + case msgUserAuthSuccess: + return true, nil, nil + default: + return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) + } + + var msg userAuthInfoRequestMsg + if err := Unmarshal(packet, &msg); err != nil { + return false, nil, err + } + + // Manually unpack the prompt/echo pairs. + rest := msg.Prompts + var prompts []string + var echos []bool + for i := 0; i < int(msg.NumPrompts); i++ { + prompt, r, ok := parseString(rest) + if !ok || len(r) == 0 { + return false, nil, errors.New("ssh: prompt format error") + } + prompts = append(prompts, string(prompt)) + echos = append(echos, r[0] != 0) + rest = r[1:] + } + + if len(rest) != 0 { + return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs") + } + + answers, err := cb(msg.User, msg.Instruction, prompts, echos) + if err != nil { + return false, nil, err + } + + if len(answers) != len(prompts) { + return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") + } + responseLength := 1 + 4 + for _, a := range answers { + responseLength += stringLength(len(a)) + } + serialized := make([]byte, responseLength) + p := serialized + p[0] = msgUserAuthInfoResponse + p = p[1:] + p = marshalUint32(p, uint32(len(answers))) + for _, a := range answers { + p = marshalString(p, []byte(a)) + } + + if err := c.writePacket(serialized); err != nil { + return false, nil, err + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/client_auth_test.go b/vendor/golang.org/x/crypto/ssh/client_auth_test.go new file mode 100644 index 0000000..c92b587 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client_auth_test.go @@ -0,0 +1,393 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto/rand" + "errors" + "fmt" + "strings" + "testing" +) + +type keyboardInteractive map[string]string + +func (cr keyboardInteractive) Challenge(user string, instruction string, questions []string, echos []bool) ([]string, error) { + var answers []string + for _, q := range questions { + answers = append(answers, cr[q]) + } + return answers, nil +} + +// reused internally by tests +var clientPassword = "tiger" + +// tryAuth runs a handshake with a given config against an SSH server +// with config serverConfig +func tryAuth(t *testing.T, config *ClientConfig) error { + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + certChecker := CertChecker{ + IsAuthority: func(k PublicKey) bool { + return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal()) + }, + UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { + if conn.User() == "testuser" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) { + return nil, nil + } + + return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User()) + }, + IsRevoked: func(c *Certificate) bool { + return c.Serial == 666 + }, + } + + serverConfig := &ServerConfig{ + PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) { + if conn.User() == "testuser" && string(pass) == clientPassword { + return nil, nil + } + return nil, errors.New("password auth failed") + }, + PublicKeyCallback: certChecker.Authenticate, + KeyboardInteractiveCallback: func(conn ConnMetadata, challenge KeyboardInteractiveChallenge) (*Permissions, error) { + ans, err := challenge("user", + "instruction", + []string{"question1", "question2"}, + []bool{true, true}) + if err != nil { + return nil, err + } + ok := conn.User() == "testuser" && ans[0] == "answer1" && ans[1] == "answer2" + if ok { + challenge("user", "motd", nil, nil) + return nil, nil + } + return nil, errors.New("keyboard-interactive failed") + }, + AuthLogCallback: func(conn ConnMetadata, method string, err error) { + t.Logf("user %q, method %q: %v", conn.User(), method, err) + }, + } + serverConfig.AddHostKey(testSigners["rsa"]) + + go newServer(c1, serverConfig) + _, _, _, err = NewClientConn(c2, "", config) + return err +} + +func TestClientAuthPublicKey(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(testSigners["rsa"]), + }, + } + if err := tryAuth(t, config); err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } +} + +func TestAuthMethodPassword(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + Password(clientPassword), + }, + } + + if err := tryAuth(t, config); err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } +} + +func TestAuthMethodFallback(t *testing.T) { + var passwordCalled bool + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(testSigners["rsa"]), + PasswordCallback( + func() (string, error) { + passwordCalled = true + return "WRONG", nil + }), + }, + } + + if err := tryAuth(t, config); err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + + if passwordCalled { + t.Errorf("password auth tried before public-key auth.") + } +} + +func TestAuthMethodWrongPassword(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + Password("wrong"), + PublicKeys(testSigners["rsa"]), + }, + } + + if err := tryAuth(t, config); err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } +} + +func TestAuthMethodKeyboardInteractive(t *testing.T) { + answers := keyboardInteractive(map[string]string{ + "question1": "answer1", + "question2": "answer2", + }) + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + KeyboardInteractive(answers.Challenge), + }, + } + + if err := tryAuth(t, config); err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } +} + +func TestAuthMethodWrongKeyboardInteractive(t *testing.T) { + answers := keyboardInteractive(map[string]string{ + "question1": "answer1", + "question2": "WRONG", + }) + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + KeyboardInteractive(answers.Challenge), + }, + } + + if err := tryAuth(t, config); err == nil { + t.Fatalf("wrong answers should not have authenticated with KeyboardInteractive") + } +} + +// the mock server will only authenticate ssh-rsa keys +func TestAuthMethodInvalidPublicKey(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(testSigners["dsa"]), + }, + } + + if err := tryAuth(t, config); err == nil { + t.Fatalf("dsa private key should not have authenticated with rsa public key") + } +} + +// the client should authenticate with the second key +func TestAuthMethodRSAandDSA(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(testSigners["dsa"], testSigners["rsa"]), + }, + } + if err := tryAuth(t, config); err != nil { + t.Fatalf("client could not authenticate with rsa key: %v", err) + } +} + +func TestClientHMAC(t *testing.T) { + for _, mac := range supportedMACs { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(testSigners["rsa"]), + }, + Config: Config{ + MACs: []string{mac}, + }, + } + if err := tryAuth(t, config); err != nil { + t.Fatalf("client could not authenticate with mac algo %s: %v", mac, err) + } + } +} + +// issue 4285. +func TestClientUnsupportedCipher(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(), + }, + Config: Config{ + Ciphers: []string{"aes128-cbc"}, // not currently supported + }, + } + if err := tryAuth(t, config); err == nil { + t.Errorf("expected no ciphers in common") + } +} + +func TestClientUnsupportedKex(t *testing.T) { + config := &ClientConfig{ + User: "testuser", + Auth: []AuthMethod{ + PublicKeys(), + }, + Config: Config{ + KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported + }, + } + if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "no common algorithms") { + t.Errorf("got %v, expected 'no common algorithms'", err) + } +} + +func TestClientLoginCert(t *testing.T) { + cert := &Certificate{ + Key: testPublicKeys["rsa"], + ValidBefore: CertTimeInfinity, + CertType: UserCert, + } + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + certSigner, err := NewCertSigner(cert, testSigners["rsa"]) + if err != nil { + t.Fatalf("NewCertSigner: %v", err) + } + + clientConfig := &ClientConfig{ + User: "user", + } + clientConfig.Auth = append(clientConfig.Auth, PublicKeys(certSigner)) + + t.Log("should succeed") + if err := tryAuth(t, clientConfig); err != nil { + t.Errorf("cert login failed: %v", err) + } + + t.Log("corrupted signature") + cert.Signature.Blob[0]++ + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("cert login passed with corrupted sig") + } + + t.Log("revoked") + cert.Serial = 666 + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("revoked cert login succeeded") + } + cert.Serial = 1 + + t.Log("sign with wrong key") + cert.SignCert(rand.Reader, testSigners["dsa"]) + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("cert login passed with non-authoritive key") + } + + t.Log("host cert") + cert.CertType = HostCert + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("cert login passed with wrong type") + } + cert.CertType = UserCert + + t.Log("principal specified") + cert.ValidPrincipals = []string{"user"} + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err != nil { + t.Errorf("cert login failed: %v", err) + } + + t.Log("wrong principal specified") + cert.ValidPrincipals = []string{"fred"} + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("cert login passed with wrong principal") + } + cert.ValidPrincipals = nil + + t.Log("added critical option") + cert.CriticalOptions = map[string]string{"root-access": "yes"} + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("cert login passed with unrecognized critical option") + } + + t.Log("allowed source address") + cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42/24"} + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err != nil { + t.Errorf("cert login with source-address failed: %v", err) + } + + t.Log("disallowed source address") + cert.CriticalOptions = map[string]string{"source-address": "127.0.0.42"} + cert.SignCert(rand.Reader, testSigners["ecdsa"]) + if err := tryAuth(t, clientConfig); err == nil { + t.Errorf("cert login with source-address succeeded") + } +} + +func testPermissionsPassing(withPermissions bool, t *testing.T) { + serverConfig := &ServerConfig{ + PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) { + if conn.User() == "nopermissions" { + return nil, nil + } else { + return &Permissions{}, nil + } + }, + } + serverConfig.AddHostKey(testSigners["rsa"]) + + clientConfig := &ClientConfig{ + Auth: []AuthMethod{ + PublicKeys(testSigners["rsa"]), + }, + } + if withPermissions { + clientConfig.User = "permissions" + } else { + clientConfig.User = "nopermissions" + } + + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + go NewClientConn(c2, "", clientConfig) + serverConn, err := newServer(c1, serverConfig) + if err != nil { + t.Fatal(err) + } + if p := serverConn.Permissions; (p != nil) != withPermissions { + t.Fatalf("withPermissions is %t, but Permissions object is %#v", withPermissions, p) + } +} + +func TestPermissionsPassing(t *testing.T) { + testPermissionsPassing(true, t) +} + +func TestNoPermissionsPassing(t *testing.T) { + testPermissionsPassing(false, t) +} diff --git a/vendor/golang.org/x/crypto/ssh/client_test.go b/vendor/golang.org/x/crypto/ssh/client_test.go new file mode 100644 index 0000000..1fe790c --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/client_test.go @@ -0,0 +1,39 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "net" + "testing" +) + +func testClientVersion(t *testing.T, config *ClientConfig, expected string) { + clientConn, serverConn := net.Pipe() + defer clientConn.Close() + receivedVersion := make(chan string, 1) + go func() { + version, err := readVersion(serverConn) + if err != nil { + receivedVersion <- "" + } else { + receivedVersion <- string(version) + } + serverConn.Close() + }() + NewClientConn(clientConn, "", config) + actual := <-receivedVersion + if actual != expected { + t.Fatalf("got %s; want %s", actual, expected) + } +} + +func TestCustomClientVersion(t *testing.T) { + version := "Test-Client-Version-0.0" + testClientVersion(t, &ClientConfig{ClientVersion: version}, version) +} + +func TestDefaultClientVersion(t *testing.T) { + testClientVersion(t, &ClientConfig{}, packageVersion) +} diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go new file mode 100644 index 0000000..0a9df1f --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -0,0 +1,365 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto" + "crypto/rand" + "fmt" + "io" + "sync" + + _ "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" +) + +// These are string constants in the SSH protocol. +const ( + compressionNone = "none" + serviceUserAuth = "ssh-userauth" + serviceSSH = "ssh-connection" +) + +// supportedCiphers specifies the supported ciphers in preference order. +var supportedCiphers = []string{ + "aes128-ctr", "aes192-ctr", "aes256-ctr", + "aes128-gcm@openssh.com", + "arcfour256", "arcfour128", +} + +// supportedKexAlgos specifies the supported key-exchange algorithms in +// preference order. +var supportedKexAlgos = []string{ + // P384 and P521 are not constant-time yet, but since we don't + // reuse ephemeral keys, using them for ECDH should be OK. + kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, + kexAlgoDH14SHA1, kexAlgoDH1SHA1, +} + +// supportedKexAlgos specifies the supported host-key algorithms (i.e. methods +// of authenticating servers) in preference order. +var supportedHostKeyAlgos = []string{ + CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, + CertAlgoECDSA384v01, CertAlgoECDSA521v01, + + KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, + KeyAlgoRSA, KeyAlgoDSA, +} + +// supportedMACs specifies a default set of MAC algorithms in preference order. +// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed +// because they have reached the end of their useful life. +var supportedMACs = []string{ + "hmac-sha2-256", "hmac-sha1", "hmac-sha1-96", +} + +var supportedCompressions = []string{compressionNone} + +// hashFuncs keeps the mapping of supported algorithms to their respective +// hashes needed for signature verification. +var hashFuncs = map[string]crypto.Hash{ + KeyAlgoRSA: crypto.SHA1, + KeyAlgoDSA: crypto.SHA1, + KeyAlgoECDSA256: crypto.SHA256, + KeyAlgoECDSA384: crypto.SHA384, + KeyAlgoECDSA521: crypto.SHA512, + CertAlgoRSAv01: crypto.SHA1, + CertAlgoDSAv01: crypto.SHA1, + CertAlgoECDSA256v01: crypto.SHA256, + CertAlgoECDSA384v01: crypto.SHA384, + CertAlgoECDSA521v01: crypto.SHA512, +} + +// unexpectedMessageError results when the SSH message that we received didn't +// match what we wanted. +func unexpectedMessageError(expected, got uint8) error { + return fmt.Errorf("ssh: unexpected message type %d (expected %d)", got, expected) +} + +// parseError results from a malformed SSH message. +func parseError(tag uint8) error { + return fmt.Errorf("ssh: parse error in message type %d", tag) +} + +func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { + for _, clientAlgo := range clientAlgos { + for _, serverAlgo := range serverAlgos { + if clientAlgo == serverAlgo { + return clientAlgo, true + } + } + } + return +} + +func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) { + for _, clientCipher := range clientCiphers { + for _, serverCipher := range serverCiphers { + // reject the cipher if we have no cipherModes definition + if clientCipher == serverCipher && cipherModes[clientCipher] != nil { + return clientCipher, true + } + } + } + return +} + +type directionAlgorithms struct { + Cipher string + MAC string + Compression string +} + +type algorithms struct { + kex string + hostKey string + w directionAlgorithms + r directionAlgorithms +} + +func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms) { + var ok bool + result := &algorithms{} + result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) + if !ok { + return + } + + result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) + if !ok { + return + } + + result.w.Cipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) + if !ok { + return + } + + result.r.Cipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) + if !ok { + return + } + + result.w.MAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) + if !ok { + return + } + + result.r.MAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) + if !ok { + return + } + + result.w.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) + if !ok { + return + } + + result.r.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) + if !ok { + return + } + + return result +} + +// If rekeythreshold is too small, we can't make any progress sending +// stuff. +const minRekeyThreshold uint64 = 256 + +// Config contains configuration data common to both ServerConfig and +// ClientConfig. +type Config struct { + // Rand provides the source of entropy for cryptographic + // primitives. If Rand is nil, the cryptographic random reader + // in package crypto/rand will be used. + Rand io.Reader + + // The maximum number of bytes sent or received after which a + // new key is negotiated. It must be at least 256. If + // unspecified, 1 gigabyte is used. + RekeyThreshold uint64 + + // The allowed key exchanges algorithms. If unspecified then a + // default set of algorithms is used. + KeyExchanges []string + + // The allowed cipher algorithms. If unspecified then a sensible + // default is used. + Ciphers []string + + // The allowed MAC algorithms. If unspecified then a sensible default + // is used. + MACs []string +} + +// SetDefaults sets sensible values for unset fields in config. This is +// exported for testing: Configs passed to SSH functions are copied and have +// default values set automatically. +func (c *Config) SetDefaults() { + if c.Rand == nil { + c.Rand = rand.Reader + } + if c.Ciphers == nil { + c.Ciphers = supportedCiphers + } + var ciphers []string + for _, c := range c.Ciphers { + if cipherModes[c] != nil { + // reject the cipher if we have no cipherModes definition + ciphers = append(ciphers, c) + } + } + c.Ciphers = ciphers + + if c.KeyExchanges == nil { + c.KeyExchanges = supportedKexAlgos + } + + if c.MACs == nil { + c.MACs = supportedMACs + } + + if c.RekeyThreshold == 0 { + // RFC 4253, section 9 suggests rekeying after 1G. + c.RekeyThreshold = 1 << 30 + } + if c.RekeyThreshold < minRekeyThreshold { + c.RekeyThreshold = minRekeyThreshold + } +} + +// buildDataSignedForAuth returns the data that is signed in order to prove +// possession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + data := struct { + Session []byte + Type byte + User string + Service string + Method string + Sign bool + Algo []byte + PubKey []byte + }{ + sessionId, + msgUserAuthRequest, + req.User, + req.Service, + req.Method, + true, + algo, + pubKey, + } + return Marshal(data) +} + +func appendU16(buf []byte, n uint16) []byte { + return append(buf, byte(n>>8), byte(n)) +} + +func appendU32(buf []byte, n uint32) []byte { + return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func appendU64(buf []byte, n uint64) []byte { + return append(buf, + byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), + byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func appendInt(buf []byte, n int) []byte { + return appendU32(buf, uint32(n)) +} + +func appendString(buf []byte, s string) []byte { + buf = appendU32(buf, uint32(len(s))) + buf = append(buf, s...) + return buf +} + +func appendBool(buf []byte, b bool) []byte { + if b { + return append(buf, 1) + } + return append(buf, 0) +} + +// newCond is a helper to hide the fact that there is no usable zero +// value for sync.Cond. +func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } + +// window represents the buffer available to clients +// wishing to write to a channel. +type window struct { + *sync.Cond + win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 + writeWaiters int + closed bool +} + +// add adds win to the amount of window available +// for consumers. +func (w *window) add(win uint32) bool { + // a zero sized window adjust is a noop. + if win == 0 { + return true + } + w.L.Lock() + if w.win+win < win { + w.L.Unlock() + return false + } + w.win += win + // It is unusual that multiple goroutines would be attempting to reserve + // window space, but not guaranteed. Use broadcast to notify all waiters + // that additional window is available. + w.Broadcast() + w.L.Unlock() + return true +} + +// close sets the window to closed, so all reservations fail +// immediately. +func (w *window) close() { + w.L.Lock() + w.closed = true + w.Broadcast() + w.L.Unlock() +} + +// reserve reserves win from the available window capacity. +// If no capacity remains, reserve will block. reserve may +// return less than requested. +func (w *window) reserve(win uint32) (uint32, error) { + var err error + w.L.Lock() + w.writeWaiters++ + w.Broadcast() + for w.win == 0 && !w.closed { + w.Wait() + } + w.writeWaiters-- + if w.win < win { + win = w.win + } + w.win -= win + if w.closed { + err = io.EOF + } + w.L.Unlock() + return win, err +} + +// waitWriterBlocked waits until some goroutine is blocked for further +// writes. It is used in tests only. +func (w *window) waitWriterBlocked() { + w.Cond.L.Lock() + for w.writeWaiters == 0 { + w.Cond.Wait() + } + w.Cond.L.Unlock() +} diff --git a/vendor/golang.org/x/crypto/ssh/connection.go b/vendor/golang.org/x/crypto/ssh/connection.go new file mode 100644 index 0000000..979d919 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/connection.go @@ -0,0 +1,144 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "fmt" + "net" +) + +// OpenChannelError is returned if the other side rejects an +// OpenChannel request. +type OpenChannelError struct { + Reason RejectionReason + Message string +} + +func (e *OpenChannelError) Error() string { + return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) +} + +// ConnMetadata holds metadata for the connection. +type ConnMetadata interface { + // User returns the user ID for this connection. + // It is empty if no authentication is used. + User() string + + // SessionID returns the sesson hash, also denoted by H. + SessionID() []byte + + // ClientVersion returns the client's version string as hashed + // into the session ID. + ClientVersion() []byte + + // ServerVersion returns the server's version string as hashed + // into the session ID. + ServerVersion() []byte + + // RemoteAddr returns the remote address for this connection. + RemoteAddr() net.Addr + + // LocalAddr returns the local address for this connection. + LocalAddr() net.Addr +} + +// Conn represents an SSH connection for both server and client roles. +// Conn is the basis for implementing an application layer, such +// as ClientConn, which implements the traditional shell access for +// clients. +type Conn interface { + ConnMetadata + + // SendRequest sends a global request, and returns the + // reply. If wantReply is true, it returns the response status + // and payload. See also RFC4254, section 4. + SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) + + // OpenChannel tries to open an channel. If the request is + // rejected, it returns *OpenChannelError. On success it returns + // the SSH Channel and a Go channel for incoming, out-of-band + // requests. The Go channel must be serviced, or the + // connection will hang. + OpenChannel(name string, data []byte) (Channel, <-chan *Request, error) + + // Close closes the underlying network connection + Close() error + + // Wait blocks until the connection has shut down, and returns the + // error causing the shutdown. + Wait() error + + // TODO(hanwen): consider exposing: + // RequestKeyChange + // Disconnect +} + +// DiscardRequests consumes and rejects all requests from the +// passed-in channel. +func DiscardRequests(in <-chan *Request) { + for req := range in { + if req.WantReply { + req.Reply(false, nil) + } + } +} + +// A connection represents an incoming connection. +type connection struct { + transport *handshakeTransport + sshConn + + // The connection protocol. + *mux +} + +func (c *connection) Close() error { + return c.sshConn.conn.Close() +} + +// sshconn provides net.Conn metadata, but disallows direct reads and +// writes. +type sshConn struct { + conn net.Conn + + user string + sessionID []byte + clientVersion []byte + serverVersion []byte +} + +func dup(src []byte) []byte { + dst := make([]byte, len(src)) + copy(dst, src) + return dst +} + +func (c *sshConn) User() string { + return c.user +} + +func (c *sshConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *sshConn) Close() error { + return c.conn.Close() +} + +func (c *sshConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *sshConn) SessionID() []byte { + return dup(c.sessionID) +} + +func (c *sshConn) ClientVersion() []byte { + return dup(c.clientVersion) +} + +func (c *sshConn) ServerVersion() []byte { + return dup(c.serverVersion) +} diff --git a/vendor/golang.org/x/crypto/ssh/doc.go b/vendor/golang.org/x/crypto/ssh/doc.go new file mode 100644 index 0000000..d348062 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/doc.go @@ -0,0 +1,18 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package ssh implements an SSH client and server. + +SSH is a transport security protocol, an authentication protocol and a +family of application protocols. The most typical application level +protocol is a remote shell and this is specifically implemented. However, +the multiplexed nature of SSH is exposed to users that wish to support +others. + +References: + [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD + [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 +*/ +package ssh // import "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" diff --git a/vendor/golang.org/x/crypto/ssh/example_test.go b/vendor/golang.org/x/crypto/ssh/example_test.go new file mode 100644 index 0000000..97e1aa4 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/example_test.go @@ -0,0 +1,211 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh" + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh/terminal" +) + +func ExampleNewServerConn() { + // An SSH server is represented by a ServerConfig, which holds + // certificate details and handles authentication of ServerConns. + config := &ssh.ServerConfig{ + PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { + // Should use constant-time compare (or better, salt+hash) in + // a production setting. + if c.User() == "testuser" && string(pass) == "tiger" { + return nil, nil + } + return nil, fmt.Errorf("password rejected for %q", c.User()) + }, + } + + privateBytes, err := ioutil.ReadFile("id_rsa") + if err != nil { + panic("Failed to load private key") + } + + private, err := ssh.ParsePrivateKey(privateBytes) + if err != nil { + panic("Failed to parse private key") + } + + config.AddHostKey(private) + + // Once a ServerConfig has been configured, connections can be + // accepted. + listener, err := net.Listen("tcp", "0.0.0.0:2022") + if err != nil { + panic("failed to listen for connection") + } + nConn, err := listener.Accept() + if err != nil { + panic("failed to accept incoming connection") + } + + // Before use, a handshake must be performed on the incoming + // net.Conn. + _, chans, reqs, err := ssh.NewServerConn(nConn, config) + if err != nil { + panic("failed to handshake") + } + // The incoming Request channel must be serviced. + go ssh.DiscardRequests(reqs) + + // Service the incoming Channel channel. + for newChannel := range chans { + // Channels have a type, depending on the application level + // protocol intended. In the case of a shell, the type is + // "session" and ServerShell may be used to present a simple + // terminal interface. + if newChannel.ChannelType() != "session" { + newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") + continue + } + channel, requests, err := newChannel.Accept() + if err != nil { + panic("could not accept channel.") + } + + // Sessions have out-of-band requests such as "shell", + // "pty-req" and "env". Here we handle only the + // "shell" request. + go func(in <-chan *ssh.Request) { + for req := range in { + ok := false + switch req.Type { + case "shell": + ok = true + if len(req.Payload) > 0 { + // We don't accept any + // commands, only the + // default shell. + ok = false + } + } + req.Reply(ok, nil) + } + }(requests) + + term := terminal.NewTerminal(channel, "> ") + + go func() { + defer channel.Close() + for { + line, err := term.ReadLine() + if err != nil { + break + } + fmt.Println(line) + } + }() + } +} + +func ExampleDial() { + // An SSH client is represented with a ClientConn. Currently only + // the "password" authentication method is supported. + // + // To authenticate with the remote server you must pass at least one + // implementation of AuthMethod via the Auth field in ClientConfig. + config := &ssh.ClientConfig{ + User: "username", + Auth: []ssh.AuthMethod{ + ssh.Password("yourpassword"), + }, + } + client, err := ssh.Dial("tcp", "yourserver.com:22", config) + if err != nil { + panic("Failed to dial: " + err.Error()) + } + + // Each ClientConn can support multiple interactive sessions, + // represented by a Session. + session, err := client.NewSession() + if err != nil { + panic("Failed to create session: " + err.Error()) + } + defer session.Close() + + // Once a Session is created, you can execute a single command on + // the remote side using the Run method. + var b bytes.Buffer + session.Stdout = &b + if err := session.Run("/usr/bin/whoami"); err != nil { + panic("Failed to run: " + err.Error()) + } + fmt.Println(b.String()) +} + +func ExampleClient_Listen() { + config := &ssh.ClientConfig{ + User: "username", + Auth: []ssh.AuthMethod{ + ssh.Password("password"), + }, + } + // Dial your ssh server. + conn, err := ssh.Dial("tcp", "localhost:22", config) + if err != nil { + log.Fatalf("unable to connect: %s", err) + } + defer conn.Close() + + // Request the remote side to open port 8080 on all interfaces. + l, err := conn.Listen("tcp", "0.0.0.0:8080") + if err != nil { + log.Fatalf("unable to register tcp forward: %v", err) + } + defer l.Close() + + // Serve HTTP with your SSH server acting as a reverse proxy. + http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + fmt.Fprintf(resp, "Hello world!\n") + })) +} + +func ExampleSession_RequestPty() { + // Create client config + config := &ssh.ClientConfig{ + User: "username", + Auth: []ssh.AuthMethod{ + ssh.Password("password"), + }, + } + // Connect to ssh server + conn, err := ssh.Dial("tcp", "localhost:22", config) + if err != nil { + log.Fatalf("unable to connect: %s", err) + } + defer conn.Close() + // Create a session + session, err := conn.NewSession() + if err != nil { + log.Fatalf("unable to create session: %s", err) + } + defer session.Close() + // Set up terminal modes + modes := ssh.TerminalModes{ + ssh.ECHO: 0, // disable echoing + ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud + ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud + } + // Request pseudo terminal + if err := session.RequestPty("xterm", 80, 40, modes); err != nil { + log.Fatalf("request for pseudo terminal failed: %s", err) + } + // Start remote shell + if err := session.Shell(); err != nil { + log.Fatalf("failed to start shell: %s", err) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go new file mode 100644 index 0000000..50461be --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -0,0 +1,404 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto/rand" + "errors" + "fmt" + "io" + "log" + "net" + "sync" +) + +// debugHandshake, if set, prints messages sent and received. Key +// exchange messages are printed as if DH were used, so the debug +// messages are wrong when using ECDH. +const debugHandshake = false + +// keyingTransport is a packet based transport that supports key +// changes. It need not be thread-safe. It should pass through +// msgNewKeys in both directions. +type keyingTransport interface { + packetConn + + // prepareKeyChange sets up a key change. The key change for a + // direction will be effected if a msgNewKeys message is sent + // or received. + prepareKeyChange(*algorithms, *kexResult) error + + // getSessionID returns the session ID. prepareKeyChange must + // have been called once. + getSessionID() []byte +} + +// rekeyingTransport is the interface of handshakeTransport that we +// (internally) expose to ClientConn and ServerConn. +type rekeyingTransport interface { + packetConn + + // requestKeyChange asks the remote side to change keys. All + // writes are blocked until the key change succeeds, which is + // signaled by reading a msgNewKeys. + requestKeyChange() error + + // getSessionID returns the session ID. This is only valid + // after the first key change has completed. + getSessionID() []byte +} + +// handshakeTransport implements rekeying on top of a keyingTransport +// and offers a thread-safe writePacket() interface. +type handshakeTransport struct { + conn keyingTransport + config *Config + + serverVersion []byte + clientVersion []byte + + // hostKeys is non-empty if we are the server. In that case, + // it contains all host keys that can be used to sign the + // connection. + hostKeys []Signer + + // hostKeyAlgorithms is non-empty if we are the client. In that case, + // we accept these key types from the server as host key. + hostKeyAlgorithms []string + + // On read error, incoming is closed, and readError is set. + incoming chan []byte + readError error + + // data for host key checking + hostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error + dialAddress string + remoteAddr net.Addr + + readSinceKex uint64 + + // Protects the writing side of the connection + mu sync.Mutex + cond *sync.Cond + sentInitPacket []byte + sentInitMsg *kexInitMsg + writtenSinceKex uint64 + writeError error +} + +func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { + t := &handshakeTransport{ + conn: conn, + serverVersion: serverVersion, + clientVersion: clientVersion, + incoming: make(chan []byte, 16), + config: config, + } + t.cond = sync.NewCond(&t.mu) + return t +} + +func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ClientConfig, dialAddr string, addr net.Addr) *handshakeTransport { + t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) + t.dialAddress = dialAddr + t.remoteAddr = addr + t.hostKeyCallback = config.HostKeyCallback + if config.HostKeyAlgorithms != nil { + t.hostKeyAlgorithms = config.HostKeyAlgorithms + } else { + t.hostKeyAlgorithms = supportedHostKeyAlgos + } + go t.readLoop() + return t +} + +func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport { + t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion) + t.hostKeys = config.hostKeys + go t.readLoop() + return t +} + +func (t *handshakeTransport) getSessionID() []byte { + return t.conn.getSessionID() +} + +func (t *handshakeTransport) id() string { + if len(t.hostKeys) > 0 { + return "server" + } + return "client" +} + +func (t *handshakeTransport) readPacket() ([]byte, error) { + p, ok := <-t.incoming + if !ok { + return nil, t.readError + } + return p, nil +} + +func (t *handshakeTransport) readLoop() { + for { + p, err := t.readOnePacket() + if err != nil { + t.readError = err + close(t.incoming) + break + } + if p[0] == msgIgnore || p[0] == msgDebug { + continue + } + t.incoming <- p + } +} + +func (t *handshakeTransport) readOnePacket() ([]byte, error) { + if t.readSinceKex > t.config.RekeyThreshold { + if err := t.requestKeyChange(); err != nil { + return nil, err + } + } + + p, err := t.conn.readPacket() + if err != nil { + return nil, err + } + + t.readSinceKex += uint64(len(p)) + if debugHandshake { + msg, err := decode(p) + log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err) + } + if p[0] != msgKexInit { + return p, nil + } + err = t.enterKeyExchange(p) + + t.mu.Lock() + if err != nil { + // drop connection + t.conn.Close() + t.writeError = err + } + + if debugHandshake { + log.Printf("%s exited key exchange, err %v", t.id(), err) + } + + // Unblock writers. + t.sentInitMsg = nil + t.sentInitPacket = nil + t.cond.Broadcast() + t.writtenSinceKex = 0 + t.mu.Unlock() + + if err != nil { + return nil, err + } + + t.readSinceKex = 0 + return []byte{msgNewKeys}, nil +} + +// sendKexInit sends a key change message, and returns the message +// that was sent. After initiating the key change, all writes will be +// blocked until the change is done, and a failed key change will +// close the underlying transport. This function is safe for +// concurrent use by multiple goroutines. +func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) { + t.mu.Lock() + defer t.mu.Unlock() + return t.sendKexInitLocked() +} + +func (t *handshakeTransport) requestKeyChange() error { + _, _, err := t.sendKexInit() + return err +} + +// sendKexInitLocked sends a key change message. t.mu must be locked +// while this happens. +func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) { + // kexInits may be sent either in response to the other side, + // or because our side wants to initiate a key change, so we + // may have already sent a kexInit. In that case, don't send a + // second kexInit. + if t.sentInitMsg != nil { + return t.sentInitMsg, t.sentInitPacket, nil + } + msg := &kexInitMsg{ + KexAlgos: t.config.KeyExchanges, + CiphersClientServer: t.config.Ciphers, + CiphersServerClient: t.config.Ciphers, + MACsClientServer: t.config.MACs, + MACsServerClient: t.config.MACs, + CompressionClientServer: supportedCompressions, + CompressionServerClient: supportedCompressions, + } + io.ReadFull(rand.Reader, msg.Cookie[:]) + + if len(t.hostKeys) > 0 { + for _, k := range t.hostKeys { + msg.ServerHostKeyAlgos = append( + msg.ServerHostKeyAlgos, k.PublicKey().Type()) + } + } else { + msg.ServerHostKeyAlgos = t.hostKeyAlgorithms + } + packet := Marshal(msg) + + // writePacket destroys the contents, so save a copy. + packetCopy := make([]byte, len(packet)) + copy(packetCopy, packet) + + if err := t.conn.writePacket(packetCopy); err != nil { + return nil, nil, err + } + + t.sentInitMsg = msg + t.sentInitPacket = packet + return msg, packet, nil +} + +func (t *handshakeTransport) writePacket(p []byte) error { + t.mu.Lock() + defer t.mu.Unlock() + + if t.writtenSinceKex > t.config.RekeyThreshold { + t.sendKexInitLocked() + } + for t.sentInitMsg != nil { + t.cond.Wait() + } + if t.writeError != nil { + return t.writeError + } + t.writtenSinceKex += uint64(len(p)) + + switch p[0] { + case msgKexInit: + return errors.New("ssh: only handshakeTransport can send kexInit") + case msgNewKeys: + return errors.New("ssh: only handshakeTransport can send newKeys") + default: + return t.conn.writePacket(p) + } +} + +func (t *handshakeTransport) Close() error { + return t.conn.Close() +} + +// enterKeyExchange runs the key exchange. +func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { + if debugHandshake { + log.Printf("%s entered key exchange", t.id()) + } + myInit, myInitPacket, err := t.sendKexInit() + if err != nil { + return err + } + + otherInit := &kexInitMsg{} + if err := Unmarshal(otherInitPacket, otherInit); err != nil { + return err + } + + magics := handshakeMagics{ + clientVersion: t.clientVersion, + serverVersion: t.serverVersion, + clientKexInit: otherInitPacket, + serverKexInit: myInitPacket, + } + + clientInit := otherInit + serverInit := myInit + if len(t.hostKeys) == 0 { + clientInit = myInit + serverInit = otherInit + + magics.clientKexInit = myInitPacket + magics.serverKexInit = otherInitPacket + } + + algs := findAgreedAlgorithms(clientInit, serverInit) + if algs == nil { + return errors.New("ssh: no common algorithms") + } + + // We don't send FirstKexFollows, but we handle receiving it. + if otherInit.FirstKexFollows && algs.kex != otherInit.KexAlgos[0] { + // other side sent a kex message for the wrong algorithm, + // which we have to ignore. + if _, err := t.conn.readPacket(); err != nil { + return err + } + } + + kex, ok := kexAlgoMap[algs.kex] + if !ok { + return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex) + } + + var result *kexResult + if len(t.hostKeys) > 0 { + result, err = t.server(kex, algs, &magics) + } else { + result, err = t.client(kex, algs, &magics) + } + + if err != nil { + return err + } + + t.conn.prepareKeyChange(algs, result) + if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { + return err + } + if packet, err := t.conn.readPacket(); err != nil { + return err + } else if packet[0] != msgNewKeys { + return unexpectedMessageError(msgNewKeys, packet[0]) + } + return nil +} + +func (t *handshakeTransport) server(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { + var hostKey Signer + for _, k := range t.hostKeys { + if algs.hostKey == k.PublicKey().Type() { + hostKey = k + } + } + + r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey) + return r, err +} + +func (t *handshakeTransport) client(kex kexAlgorithm, algs *algorithms, magics *handshakeMagics) (*kexResult, error) { + result, err := kex.Client(t.conn, t.config.Rand, magics) + if err != nil { + return nil, err + } + + hostKey, err := ParsePublicKey(result.HostKey) + if err != nil { + return nil, err + } + + if err := verifyHostKeySignature(hostKey, result); err != nil { + return nil, err + } + + if t.hostKeyCallback != nil { + err = t.hostKeyCallback(t.dialAddress, t.remoteAddr, hostKey) + if err != nil { + return nil, err + } + } + + return result, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/handshake_test.go b/vendor/golang.org/x/crypto/ssh/handshake_test.go new file mode 100644 index 0000000..3202371 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/handshake_test.go @@ -0,0 +1,316 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto/rand" + "fmt" + "net" + "runtime" + "testing" +) + +type testChecker struct { + calls []string +} + +func (t *testChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error { + if dialAddr == "bad" { + return fmt.Errorf("dialAddr is bad") + } + + if tcpAddr, ok := addr.(*net.TCPAddr); !ok || tcpAddr == nil { + return fmt.Errorf("testChecker: got %T want *net.TCPAddr", addr) + } + + t.calls = append(t.calls, fmt.Sprintf("%s %v %s %x", dialAddr, addr, key.Type(), key.Marshal())) + + return nil +} + +// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and +// therefore is buffered (net.Pipe deadlocks if both sides start with +// a write.) +func netPipe() (net.Conn, net.Conn, error) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, nil, err + } + defer listener.Close() + c1, err := net.Dial("tcp", listener.Addr().String()) + if err != nil { + return nil, nil, err + } + + c2, err := listener.Accept() + if err != nil { + c1.Close() + return nil, nil, err + } + + return c1, c2, nil +} + +func handshakePair(clientConf *ClientConfig, addr string) (client *handshakeTransport, server *handshakeTransport, err error) { + a, b, err := netPipe() + if err != nil { + return nil, nil, err + } + + trC := newTransport(a, rand.Reader, true) + trS := newTransport(b, rand.Reader, false) + clientConf.SetDefaults() + + v := []byte("version") + client = newClientTransport(trC, v, v, clientConf, addr, a.RemoteAddr()) + + serverConf := &ServerConfig{} + serverConf.AddHostKey(testSigners["ecdsa"]) + serverConf.AddHostKey(testSigners["rsa"]) + serverConf.SetDefaults() + server = newServerTransport(trS, v, v, serverConf) + + return client, server, nil +} + +func TestHandshakeBasic(t *testing.T) { + if runtime.GOOS == "plan9" { + t.Skip("see golang.org/issue/7237") + } + checker := &testChecker{} + trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") + if err != nil { + t.Fatalf("handshakePair: %v", err) + } + + defer trC.Close() + defer trS.Close() + + go func() { + // Client writes a bunch of stuff, and does a key + // change in the middle. This should not confuse the + // handshake in progress + for i := 0; i < 10; i++ { + p := []byte{msgRequestSuccess, byte(i)} + if err := trC.writePacket(p); err != nil { + t.Fatalf("sendPacket: %v", err) + } + if i == 5 { + // halfway through, we request a key change. + _, _, err := trC.sendKexInit() + if err != nil { + t.Fatalf("sendKexInit: %v", err) + } + } + } + trC.Close() + }() + + // Server checks that client messages come in cleanly + i := 0 + for { + p, err := trS.readPacket() + if err != nil { + break + } + if p[0] == msgNewKeys { + continue + } + want := []byte{msgRequestSuccess, byte(i)} + if bytes.Compare(p, want) != 0 { + t.Errorf("message %d: got %q, want %q", i, p, want) + } + i++ + } + if i != 10 { + t.Errorf("received %d messages, want 10.", i) + } + + // If all went well, we registered exactly 1 key change. + if len(checker.calls) != 1 { + t.Fatalf("got %d host key checks, want 1", len(checker.calls)) + } + + pub := testSigners["ecdsa"].PublicKey() + want := fmt.Sprintf("%s %v %s %x", "addr", trC.remoteAddr, pub.Type(), pub.Marshal()) + if want != checker.calls[0] { + t.Errorf("got %q want %q for host key check", checker.calls[0], want) + } +} + +func TestHandshakeError(t *testing.T) { + checker := &testChecker{} + trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "bad") + if err != nil { + t.Fatalf("handshakePair: %v", err) + } + defer trC.Close() + defer trS.Close() + + // send a packet + packet := []byte{msgRequestSuccess, 42} + if err := trC.writePacket(packet); err != nil { + t.Errorf("writePacket: %v", err) + } + + // Now request a key change. + _, _, err = trC.sendKexInit() + if err != nil { + t.Errorf("sendKexInit: %v", err) + } + + // the key change will fail, and afterwards we can't write. + if err := trC.writePacket([]byte{msgRequestSuccess, 43}); err == nil { + t.Errorf("writePacket after botched rekey succeeded.") + } + + readback, err := trS.readPacket() + if err != nil { + t.Fatalf("server closed too soon: %v", err) + } + if bytes.Compare(readback, packet) != 0 { + t.Errorf("got %q want %q", readback, packet) + } + readback, err = trS.readPacket() + if err == nil { + t.Errorf("got a message %q after failed key change", readback) + } +} + +func TestHandshakeTwice(t *testing.T) { + checker := &testChecker{} + trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr") + if err != nil { + t.Fatalf("handshakePair: %v", err) + } + + defer trC.Close() + defer trS.Close() + + // send a packet + packet := make([]byte, 5) + packet[0] = msgRequestSuccess + if err := trC.writePacket(packet); err != nil { + t.Errorf("writePacket: %v", err) + } + + // Now request a key change. + _, _, err = trC.sendKexInit() + if err != nil { + t.Errorf("sendKexInit: %v", err) + } + + // Send another packet. Use a fresh one, since writePacket destroys. + packet = make([]byte, 5) + packet[0] = msgRequestSuccess + if err := trC.writePacket(packet); err != nil { + t.Errorf("writePacket: %v", err) + } + + // 2nd key change. + _, _, err = trC.sendKexInit() + if err != nil { + t.Errorf("sendKexInit: %v", err) + } + + packet = make([]byte, 5) + packet[0] = msgRequestSuccess + if err := trC.writePacket(packet); err != nil { + t.Errorf("writePacket: %v", err) + } + + packet = make([]byte, 5) + packet[0] = msgRequestSuccess + for i := 0; i < 5; i++ { + msg, err := trS.readPacket() + if err != nil { + t.Fatalf("server closed too soon: %v", err) + } + if msg[0] == msgNewKeys { + continue + } + + if bytes.Compare(msg, packet) != 0 { + t.Errorf("packet %d: got %q want %q", i, msg, packet) + } + } + if len(checker.calls) != 2 { + t.Errorf("got %d key changes, want 2", len(checker.calls)) + } +} + +func TestHandshakeAutoRekeyWrite(t *testing.T) { + checker := &testChecker{} + clientConf := &ClientConfig{HostKeyCallback: checker.Check} + clientConf.RekeyThreshold = 500 + trC, trS, err := handshakePair(clientConf, "addr") + if err != nil { + t.Fatalf("handshakePair: %v", err) + } + defer trC.Close() + defer trS.Close() + + for i := 0; i < 5; i++ { + packet := make([]byte, 251) + packet[0] = msgRequestSuccess + if err := trC.writePacket(packet); err != nil { + t.Errorf("writePacket: %v", err) + } + } + + j := 0 + for ; j < 5; j++ { + _, err := trS.readPacket() + if err != nil { + break + } + } + + if j != 5 { + t.Errorf("got %d, want 5 messages", j) + } + + if len(checker.calls) != 2 { + t.Errorf("got %d key changes, wanted 2", len(checker.calls)) + } +} + +type syncChecker struct { + called chan int +} + +func (t *syncChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error { + t.called <- 1 + return nil +} + +func TestHandshakeAutoRekeyRead(t *testing.T) { + sync := &syncChecker{make(chan int, 2)} + clientConf := &ClientConfig{ + HostKeyCallback: sync.Check, + } + clientConf.RekeyThreshold = 500 + + trC, trS, err := handshakePair(clientConf, "addr") + if err != nil { + t.Fatalf("handshakePair: %v", err) + } + defer trC.Close() + defer trS.Close() + + packet := make([]byte, 501) + packet[0] = msgRequestSuccess + if err := trS.writePacket(packet); err != nil { + t.Fatalf("writePacket: %v", err) + } + // While we read out the packet, a key change will be + // initiated. + if _, err := trC.readPacket(); err != nil { + t.Fatalf("readPacket(client): %v", err) + } + + <-sync.called +} diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go new file mode 100644 index 0000000..6a835c7 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -0,0 +1,386 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "errors" + "io" + "math/big" +) + +const ( + kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" + kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" + kexAlgoECDH256 = "ecdh-sha2-nistp256" + kexAlgoECDH384 = "ecdh-sha2-nistp384" + kexAlgoECDH521 = "ecdh-sha2-nistp521" +) + +// kexResult captures the outcome of a key exchange. +type kexResult struct { + // Session hash. See also RFC 4253, section 8. + H []byte + + // Shared secret. See also RFC 4253, section 8. + K []byte + + // Host key as hashed into H. + HostKey []byte + + // Signature of H. + Signature []byte + + // A cryptographic hash function that matches the security + // level of the key exchange algorithm. It is used for + // calculating H, and for deriving keys from H and K. + Hash crypto.Hash + + // The session ID, which is the first H computed. This is used + // to signal data inside transport. + SessionID []byte +} + +// handshakeMagics contains data that is always included in the +// session hash. +type handshakeMagics struct { + clientVersion, serverVersion []byte + clientKexInit, serverKexInit []byte +} + +func (m *handshakeMagics) write(w io.Writer) { + writeString(w, m.clientVersion) + writeString(w, m.serverVersion) + writeString(w, m.clientKexInit) + writeString(w, m.serverKexInit) +} + +// kexAlgorithm abstracts different key exchange algorithms. +type kexAlgorithm interface { + // Server runs server-side key agreement, signing the result + // with a hostkey. + Server(p packetConn, rand io.Reader, magics *handshakeMagics, s Signer) (*kexResult, error) + + // Client runs the client-side key agreement. Caller is + // responsible for verifying the host key signature. + Client(p packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) +} + +// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. +type dhGroup struct { + g, p *big.Int +} + +func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { + if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 { + return nil, errors.New("ssh: DH parameter out of bounds") + } + return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil +} + +func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { + hashFunc := crypto.SHA1 + + x, err := rand.Int(randSource, group.p) + if err != nil { + return nil, err + } + X := new(big.Int).Exp(group.g, x, group.p) + kexDHInit := kexDHInitMsg{ + X: X, + } + if err := c.writePacket(Marshal(&kexDHInit)); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexDHReply kexDHReplyMsg + if err = Unmarshal(packet, &kexDHReply); err != nil { + return nil, err + } + + kInt, err := group.diffieHellman(kexDHReply.Y, x) + if err != nil { + return nil, err + } + + h := hashFunc.New() + magics.write(h) + writeString(h, kexDHReply.HostKey) + writeInt(h, X) + writeInt(h, kexDHReply.Y) + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: kexDHReply.HostKey, + Signature: kexDHReply.Signature, + Hash: crypto.SHA1, + }, nil +} + +func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + hashFunc := crypto.SHA1 + packet, err := c.readPacket() + if err != nil { + return + } + var kexDHInit kexDHInitMsg + if err = Unmarshal(packet, &kexDHInit); err != nil { + return + } + + y, err := rand.Int(randSource, group.p) + if err != nil { + return + } + + Y := new(big.Int).Exp(group.g, y, group.p) + kInt, err := group.diffieHellman(kexDHInit.X, y) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + h := hashFunc.New() + magics.write(h) + writeString(h, hostKeyBytes) + writeInt(h, kexDHInit.X) + writeInt(h, Y) + + K := make([]byte, intLength(kInt)) + marshalInt(K, kInt) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, randSource, H) + if err != nil { + return nil, err + } + + kexDHReply := kexDHReplyMsg{ + HostKey: hostKeyBytes, + Y: Y, + Signature: sig, + } + packet = Marshal(&kexDHReply) + + err = c.writePacket(packet) + return &kexResult{ + H: H, + K: K, + HostKey: hostKeyBytes, + Signature: sig, + Hash: crypto.SHA1, + }, nil +} + +// ecdh performs Elliptic Curve Diffie-Hellman key exchange as +// described in RFC 5656, section 4. +type ecdh struct { + curve elliptic.Curve +} + +func (kex *ecdh) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) { + ephKey, err := ecdsa.GenerateKey(kex.curve, rand) + if err != nil { + return nil, err + } + + kexInit := kexECDHInitMsg{ + ClientPubKey: elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y), + } + + serialized := Marshal(&kexInit) + if err := c.writePacket(serialized); err != nil { + return nil, err + } + + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var reply kexECDHReplyMsg + if err = Unmarshal(packet, &reply); err != nil { + return nil, err + } + + x, y, err := unmarshalECKey(kex.curve, reply.EphemeralPubKey) + if err != nil { + return nil, err + } + + // generate shared secret + secret, _ := kex.curve.ScalarMult(x, y, ephKey.D.Bytes()) + + h := ecHash(kex.curve).New() + magics.write(h) + writeString(h, reply.HostKey) + writeString(h, kexInit.ClientPubKey) + writeString(h, reply.EphemeralPubKey) + K := make([]byte, intLength(secret)) + marshalInt(K, secret) + h.Write(K) + + return &kexResult{ + H: h.Sum(nil), + K: K, + HostKey: reply.HostKey, + Signature: reply.Signature, + Hash: ecHash(kex.curve), + }, nil +} + +// unmarshalECKey parses and checks an EC key. +func unmarshalECKey(curve elliptic.Curve, pubkey []byte) (x, y *big.Int, err error) { + x, y = elliptic.Unmarshal(curve, pubkey) + if x == nil { + return nil, nil, errors.New("ssh: elliptic.Unmarshal failure") + } + if !validateECPublicKey(curve, x, y) { + return nil, nil, errors.New("ssh: public key not on curve") + } + return x, y, nil +} + +// validateECPublicKey checks that the point is a valid public key for +// the given curve. See [SEC1], 3.2.2 +func validateECPublicKey(curve elliptic.Curve, x, y *big.Int) bool { + if x.Sign() == 0 && y.Sign() == 0 { + return false + } + + if x.Cmp(curve.Params().P) >= 0 { + return false + } + + if y.Cmp(curve.Params().P) >= 0 { + return false + } + + if !curve.IsOnCurve(x, y) { + return false + } + + // We don't check if N * PubKey == 0, since + // + // - the NIST curves have cofactor = 1, so this is implicit. + // (We don't foresee an implementation that supports non NIST + // curves) + // + // - for ephemeral keys, we don't need to worry about small + // subgroup attacks. + return true +} + +func (kex *ecdh) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) { + packet, err := c.readPacket() + if err != nil { + return nil, err + } + + var kexECDHInit kexECDHInitMsg + if err = Unmarshal(packet, &kexECDHInit); err != nil { + return nil, err + } + + clientX, clientY, err := unmarshalECKey(kex.curve, kexECDHInit.ClientPubKey) + if err != nil { + return nil, err + } + + // We could cache this key across multiple users/multiple + // connection attempts, but the benefit is small. OpenSSH + // generates a new key for each incoming connection. + ephKey, err := ecdsa.GenerateKey(kex.curve, rand) + if err != nil { + return nil, err + } + + hostKeyBytes := priv.PublicKey().Marshal() + + serializedEphKey := elliptic.Marshal(kex.curve, ephKey.PublicKey.X, ephKey.PublicKey.Y) + + // generate shared secret + secret, _ := kex.curve.ScalarMult(clientX, clientY, ephKey.D.Bytes()) + + h := ecHash(kex.curve).New() + magics.write(h) + writeString(h, hostKeyBytes) + writeString(h, kexECDHInit.ClientPubKey) + writeString(h, serializedEphKey) + + K := make([]byte, intLength(secret)) + marshalInt(K, secret) + h.Write(K) + + H := h.Sum(nil) + + // H is already a hash, but the hostkey signing will apply its + // own key-specific hash algorithm. + sig, err := signAndMarshal(priv, rand, H) + if err != nil { + return nil, err + } + + reply := kexECDHReplyMsg{ + EphemeralPubKey: serializedEphKey, + HostKey: hostKeyBytes, + Signature: sig, + } + + serialized := Marshal(&reply) + if err := c.writePacket(serialized); err != nil { + return nil, err + } + + return &kexResult{ + H: H, + K: K, + HostKey: reply.HostKey, + Signature: sig, + Hash: ecHash(kex.curve), + }, nil +} + +var kexAlgoMap = map[string]kexAlgorithm{} + +func init() { + // This is the group called diffie-hellman-group1-sha1 in RFC + // 4253 and Oakley Group 2 in RFC 2409. + p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) + kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + } + + // This is the group called diffie-hellman-group14-sha1 in RFC + // 4253 and Oakley Group 14 in RFC 3526. + p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) + + kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ + g: new(big.Int).SetInt64(2), + p: p, + } + + kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} + kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()} + kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()} +} diff --git a/vendor/golang.org/x/crypto/ssh/kex_test.go b/vendor/golang.org/x/crypto/ssh/kex_test.go new file mode 100644 index 0000000..0db5f9b --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/kex_test.go @@ -0,0 +1,48 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Key exchange tests. + +import ( + "crypto/rand" + "reflect" + "testing" +) + +func TestKexes(t *testing.T) { + type kexResultErr struct { + result *kexResult + err error + } + + for name, kex := range kexAlgoMap { + a, b := memPipe() + + s := make(chan kexResultErr, 1) + c := make(chan kexResultErr, 1) + var magics handshakeMagics + go func() { + r, e := kex.Client(a, rand.Reader, &magics) + c <- kexResultErr{r, e} + }() + go func() { + r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"]) + s <- kexResultErr{r, e} + }() + + clientRes := <-c + serverRes := <-s + if clientRes.err != nil { + t.Errorf("client: %v", clientRes.err) + } + if serverRes.err != nil { + t.Errorf("server: %v", serverRes.err) + } + if !reflect.DeepEqual(clientRes.result, serverRes.result) { + t.Errorf("kex %q: mismatch %#v, %#v", name, clientRes.result, serverRes.result) + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go new file mode 100644 index 0000000..e8af511 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -0,0 +1,628 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" + "io" + "math/big" +) + +// These constants represent the algorithm names for key types supported by this +// package. +const ( + KeyAlgoRSA = "ssh-rsa" + KeyAlgoDSA = "ssh-dss" + KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" + KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" + KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" +) + +// parsePubKey parses a public key of the given algorithm. +// Use ParsePublicKey for keys with prepended algorithm. +func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { + switch algo { + case KeyAlgoRSA: + return parseRSA(in) + case KeyAlgoDSA: + return parseDSA(in) + case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: + return parseECDSA(in) + case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: + cert, err := parseCert(in, certToPrivAlgo(algo)) + if err != nil { + return nil, nil, err + } + return cert, nil, nil + } + return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err) +} + +// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format +// (see sshd(8) manual page) once the options and key type fields have been +// removed. +func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { + in = bytes.TrimSpace(in) + + i := bytes.IndexAny(in, " \t") + if i == -1 { + i = len(in) + } + base64Key := in[:i] + + key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) + n, err := base64.StdEncoding.Decode(key, base64Key) + if err != nil { + return nil, "", err + } + key = key[:n] + out, err = ParsePublicKey(key) + if err != nil { + return nil, "", err + } + comment = string(bytes.TrimSpace(in[i:])) + return out, comment, nil +} + +// ParseAuthorizedKeys parses a public key from an authorized_keys +// file used in OpenSSH according to the sshd(8) manual page. +func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { + for len(in) > 0 { + end := bytes.IndexByte(in, '\n') + if end != -1 { + rest = in[end+1:] + in = in[:end] + } else { + rest = nil + } + + end = bytes.IndexByte(in, '\r') + if end != -1 { + in = in[:end] + } + + in = bytes.TrimSpace(in) + if len(in) == 0 || in[0] == '#' { + in = rest + continue + } + + i := bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + return out, comment, options, rest, nil + } + + // No key type recognised. Maybe there's an options field at + // the beginning. + var b byte + inQuote := false + var candidateOptions []string + optionStart := 0 + for i, b = range in { + isEnd := !inQuote && (b == ' ' || b == '\t') + if (b == ',' && !inQuote) || isEnd { + if i-optionStart > 0 { + candidateOptions = append(candidateOptions, string(in[optionStart:i])) + } + optionStart = i + 1 + } + if isEnd { + break + } + if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { + inQuote = !inQuote + } + } + for i < len(in) && (in[i] == ' ' || in[i] == '\t') { + i++ + } + if i == len(in) { + // Invalid line: unmatched quote + in = rest + continue + } + + in = in[i:] + i = bytes.IndexAny(in, " \t") + if i == -1 { + in = rest + continue + } + + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + options = candidateOptions + return out, comment, options, rest, nil + } + + in = rest + continue + } + + return nil, "", nil, nil, errors.New("ssh: no key found") +} + +// ParsePublicKey parses an SSH public key formatted for use in +// the SSH wire protocol according to RFC 4253, section 6.6. +func ParsePublicKey(in []byte) (out PublicKey, err error) { + algo, in, ok := parseString(in) + if !ok { + return nil, errShortRead + } + var rest []byte + out, rest, err = parsePubKey(in, string(algo)) + if len(rest) > 0 { + return nil, errors.New("ssh: trailing junk in public key") + } + + return out, err +} + +// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH +// authorized_keys file. The return value ends with newline. +func MarshalAuthorizedKey(key PublicKey) []byte { + b := &bytes.Buffer{} + b.WriteString(key.Type()) + b.WriteByte(' ') + e := base64.NewEncoder(base64.StdEncoding, b) + e.Write(key.Marshal()) + e.Close() + b.WriteByte('\n') + return b.Bytes() +} + +// PublicKey is an abstraction of different types of public keys. +type PublicKey interface { + // Type returns the key's type, e.g. "ssh-rsa". + Type() string + + // Marshal returns the serialized key data in SSH wire format, + // with the name prefix. + Marshal() []byte + + // Verify that sig is a signature on the given data using this + // key. This function will hash the data appropriately first. + Verify(data []byte, sig *Signature) error +} + +// A Signer can create signatures that verify against a public key. +type Signer interface { + // PublicKey returns an associated PublicKey instance. + PublicKey() PublicKey + + // Sign returns raw signature for the given data. This method + // will apply the hash specified for the keytype to the data. + Sign(rand io.Reader, data []byte) (*Signature, error) +} + +type rsaPublicKey rsa.PublicKey + +func (r *rsaPublicKey) Type() string { + return "ssh-rsa" +} + +// parseRSA parses an RSA key according to RFC 4253, section 6.6. +func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + E *big.Int + N *big.Int + Rest []byte `ssh:"rest"` + } + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + if w.E.BitLen() > 24 { + return nil, nil, errors.New("ssh: exponent too large") + } + e := w.E.Int64() + if e < 3 || e&1 == 0 { + return nil, nil, errors.New("ssh: incorrect exponent") + } + + var key rsa.PublicKey + key.E = int(e) + key.N = w.N + return (*rsaPublicKey)(&key), w.Rest, nil +} + +func (r *rsaPublicKey) Marshal() []byte { + e := new(big.Int).SetInt64(int64(r.E)) + wirekey := struct { + Name string + E *big.Int + N *big.Int + }{ + KeyAlgoRSA, + e, + r.N, + } + return Marshal(&wirekey) +} + +func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != r.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) + } + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) +} + +type rsaPrivateKey struct { + *rsa.PrivateKey +} + +func (r *rsaPrivateKey) PublicKey() PublicKey { + return (*rsaPublicKey)(&r.PrivateKey.PublicKey) +} + +func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + blob, err := rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest) + if err != nil { + return nil, err + } + return &Signature{ + Format: r.PublicKey().Type(), + Blob: blob, + }, nil +} + +type dsaPublicKey dsa.PublicKey + +func (r *dsaPublicKey) Type() string { + return "ssh-dss" +} + +// parseDSA parses an DSA key according to RFC 4253, section 6.6. +func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + P, Q, G, Y *big.Int + Rest []byte `ssh:"rest"` + } + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err + } + + key := &dsaPublicKey{ + Parameters: dsa.Parameters{ + P: w.P, + Q: w.Q, + G: w.G, + }, + Y: w.Y, + } + return key, w.Rest, nil +} + +func (k *dsaPublicKey) Marshal() []byte { + w := struct { + Name string + P, Q, G, Y *big.Int + }{ + k.Type(), + k.P, + k.Q, + k.G, + k.Y, + } + + return Marshal(&w) +} + +func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + + // Per RFC 4253, section 6.6, + // The value for 'dss_signature_blob' is encoded as a string containing + // r, followed by s (which are 160-bit integers, without lengths or + // padding, unsigned, and in network byte order). + // For DSS purposes, sig.Blob should be exactly 40 bytes in length. + if len(sig.Blob) != 40 { + return errors.New("ssh: DSA signature parse error") + } + r := new(big.Int).SetBytes(sig.Blob[:20]) + s := new(big.Int).SetBytes(sig.Blob[20:]) + if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +type dsaPrivateKey struct { + *dsa.PrivateKey +} + +func (k *dsaPrivateKey) PublicKey() PublicKey { + return (*dsaPublicKey)(&k.PrivateKey.PublicKey) +} + +func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { + h := crypto.SHA1.New() + h.Write(data) + digest := h.Sum(nil) + r, s, err := dsa.Sign(rand, k.PrivateKey, digest) + if err != nil { + return nil, err + } + + sig := make([]byte, 40) + rb := r.Bytes() + sb := s.Bytes() + + copy(sig[20-len(rb):20], rb) + copy(sig[40-len(sb):], sb) + + return &Signature{ + Format: k.PublicKey().Type(), + Blob: sig, + }, nil +} + +type ecdsaPublicKey ecdsa.PublicKey + +func (key *ecdsaPublicKey) Type() string { + return "ecdsa-sha2-" + key.nistID() +} + +func (key *ecdsaPublicKey) nistID() string { + switch key.Params().BitSize { + case 256: + return "nistp256" + case 384: + return "nistp384" + case 521: + return "nistp521" + } + panic("ssh: unsupported ecdsa key size") +} + +func supportedEllipticCurve(curve elliptic.Curve) bool { + return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() +} + +// ecHash returns the hash to match the given elliptic curve, see RFC +// 5656, section 6.2.1 +func ecHash(curve elliptic.Curve) crypto.Hash { + bitSize := curve.Params().BitSize + switch { + case bitSize <= 256: + return crypto.SHA256 + case bitSize <= 384: + return crypto.SHA384 + } + return crypto.SHA512 +} + +// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. +func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { + identifier, in, ok := parseString(in) + if !ok { + return nil, nil, errShortRead + } + + key := new(ecdsa.PublicKey) + + switch string(identifier) { + case "nistp256": + key.Curve = elliptic.P256() + case "nistp384": + key.Curve = elliptic.P384() + case "nistp521": + key.Curve = elliptic.P521() + default: + return nil, nil, errors.New("ssh: unsupported curve") + } + + var keyBytes []byte + if keyBytes, in, ok = parseString(in); !ok { + return nil, nil, errShortRead + } + + key.X, key.Y = elliptic.Unmarshal(key.Curve, keyBytes) + if key.X == nil || key.Y == nil { + return nil, nil, errors.New("ssh: invalid curve point") + } + return (*ecdsaPublicKey)(key), in, nil +} + +func (key *ecdsaPublicKey) Marshal() []byte { + // See RFC 5656, section 3.1. + keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) + w := struct { + Name string + ID string + Key []byte + }{ + key.Type(), + key.nistID(), + keyBytes, + } + + return Marshal(&w) +} + +func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != key.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) + } + + h := ecHash(key.Curve).New() + h.Write(data) + digest := h.Sum(nil) + + // Per RFC 5656, section 3.1.2, + // The ecdsa_signature_blob value has the following specific encoding: + // mpint r + // mpint s + var ecSig struct { + R *big.Int + S *big.Int + } + + if err := Unmarshal(sig.Blob, &ecSig); err != nil { + return err + } + + if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) { + return nil + } + return errors.New("ssh: signature did not verify") +} + +type ecdsaPrivateKey struct { + *ecdsa.PrivateKey +} + +func (k *ecdsaPrivateKey) PublicKey() PublicKey { + return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey) +} + +func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { + h := ecHash(k.PrivateKey.PublicKey.Curve).New() + h.Write(data) + digest := h.Sum(nil) + r, s, err := ecdsa.Sign(rand, k.PrivateKey, digest) + if err != nil { + return nil, err + } + + sig := make([]byte, intLength(r)+intLength(s)) + rest := marshalInt(sig, r) + marshalInt(rest, s) + return &Signature{ + Format: k.PublicKey().Type(), + Blob: sig, + }, nil +} + +// NewSignerFromKey takes a pointer to rsa, dsa or ecdsa PrivateKey +// returns a corresponding Signer instance. EC keys should use P256, +// P384 or P521. +func NewSignerFromKey(k interface{}) (Signer, error) { + var sshKey Signer + switch t := k.(type) { + case *rsa.PrivateKey: + sshKey = &rsaPrivateKey{t} + case *dsa.PrivateKey: + sshKey = &dsaPrivateKey{t} + case *ecdsa.PrivateKey: + if !supportedEllipticCurve(t.Curve) { + return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.") + } + + sshKey = &ecdsaPrivateKey{t} + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", k) + } + return sshKey, nil +} + +// NewPublicKey takes a pointer to rsa, dsa or ecdsa PublicKey +// and returns a corresponding ssh PublicKey instance. EC keys should use P256, P384 or P521. +func NewPublicKey(k interface{}) (PublicKey, error) { + var sshKey PublicKey + switch t := k.(type) { + case *rsa.PublicKey: + sshKey = (*rsaPublicKey)(t) + case *ecdsa.PublicKey: + if !supportedEllipticCurve(t.Curve) { + return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.") + } + sshKey = (*ecdsaPublicKey)(t) + case *dsa.PublicKey: + sshKey = (*dsaPublicKey)(t) + default: + return nil, fmt.Errorf("ssh: unsupported key type %T", k) + } + return sshKey, nil +} + +// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports +// the same keys as ParseRawPrivateKey. +func ParsePrivateKey(pemBytes []byte) (Signer, error) { + key, err := ParseRawPrivateKey(pemBytes) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + +// ParseRawPrivateKey returns a private key from a PEM encoded private key. It +// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. +func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("ssh: no key found") + } + + switch block.Type { + case "RSA PRIVATE KEY": + return x509.ParsePKCS1PrivateKey(block.Bytes) + case "EC PRIVATE KEY": + return x509.ParseECPrivateKey(block.Bytes) + case "DSA PRIVATE KEY": + return ParseDSAPrivateKey(block.Bytes) + default: + return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) + } +} + +// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as +// specified by the OpenSSL DSA man page. +func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { + var k struct { + Version int + P *big.Int + Q *big.Int + G *big.Int + Priv *big.Int + Pub *big.Int + } + rest, err := asn1.Unmarshal(der, &k) + if err != nil { + return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) + } + if len(rest) > 0 { + return nil, errors.New("ssh: garbage after DSA key") + } + + return &dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: k.P, + Q: k.Q, + G: k.G, + }, + Y: k.Priv, + }, + X: k.Pub, + }, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/keys_test.go b/vendor/golang.org/x/crypto/ssh/keys_test.go new file mode 100644 index 0000000..2365193 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/keys_test.go @@ -0,0 +1,306 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "encoding/base64" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh/testdata" +) + +func rawKey(pub PublicKey) interface{} { + switch k := pub.(type) { + case *rsaPublicKey: + return (*rsa.PublicKey)(k) + case *dsaPublicKey: + return (*dsa.PublicKey)(k) + case *ecdsaPublicKey: + return (*ecdsa.PublicKey)(k) + case *Certificate: + return k + } + panic("unknown key type") +} + +func TestKeyMarshalParse(t *testing.T) { + for _, priv := range testSigners { + pub := priv.PublicKey() + roundtrip, err := ParsePublicKey(pub.Marshal()) + if err != nil { + t.Errorf("ParsePublicKey(%T): %v", pub, err) + } + + k1 := rawKey(pub) + k2 := rawKey(roundtrip) + + if !reflect.DeepEqual(k1, k2) { + t.Errorf("got %#v in roundtrip, want %#v", k2, k1) + } + } +} + +func TestUnsupportedCurves(t *testing.T) { + raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + if err != nil { + t.Fatalf("GenerateKey: %v", err) + } + + if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") { + t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err) + } + + if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") { + t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err) + } +} + +func TestNewPublicKey(t *testing.T) { + for _, k := range testSigners { + raw := rawKey(k.PublicKey()) + // Skip certificates, as NewPublicKey does not support them. + if _, ok := raw.(*Certificate); ok { + continue + } + pub, err := NewPublicKey(raw) + if err != nil { + t.Errorf("NewPublicKey(%#v): %v", raw, err) + } + if !reflect.DeepEqual(k.PublicKey(), pub) { + t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey()) + } + } +} + +func TestKeySignVerify(t *testing.T) { + for _, priv := range testSigners { + pub := priv.PublicKey() + + data := []byte("sign me") + sig, err := priv.Sign(rand.Reader, data) + if err != nil { + t.Fatalf("Sign(%T): %v", priv, err) + } + + if err := pub.Verify(data, sig); err != nil { + t.Errorf("publicKey.Verify(%T): %v", priv, err) + } + sig.Blob[5]++ + if err := pub.Verify(data, sig); err == nil { + t.Errorf("publicKey.Verify on broken sig did not fail") + } + } +} + +func TestParseRSAPrivateKey(t *testing.T) { + key := testPrivateKeys["rsa"] + + rsa, ok := key.(*rsa.PrivateKey) + if !ok { + t.Fatalf("got %T, want *rsa.PrivateKey", rsa) + } + + if err := rsa.Validate(); err != nil { + t.Errorf("Validate: %v", err) + } +} + +func TestParseECPrivateKey(t *testing.T) { + key := testPrivateKeys["ecdsa"] + + ecKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey) + } + + if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) { + t.Fatalf("public key does not validate.") + } +} + +func TestParseDSA(t *testing.T) { + // We actually exercise the ParsePrivateKey codepath here, as opposed to + // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go + // uses. + s, err := ParsePrivateKey(testdata.PEMBytes["dsa"]) + if err != nil { + t.Fatalf("ParsePrivateKey returned error: %s", err) + } + + data := []byte("sign me") + sig, err := s.Sign(rand.Reader, data) + if err != nil { + t.Fatalf("dsa.Sign: %v", err) + } + + if err := s.PublicKey().Verify(data, sig); err != nil { + t.Errorf("Verify failed: %v", err) + } +} + +// Tests for authorized_keys parsing. + +// getTestKey returns a public key, and its base64 encoding. +func getTestKey() (PublicKey, string) { + k := testPublicKeys["rsa"] + + b := &bytes.Buffer{} + e := base64.NewEncoder(base64.StdEncoding, b) + e.Write(k.Marshal()) + e.Close() + + return k, b.String() +} + +func TestMarshalParsePublicKey(t *testing.T) { + pub, pubSerialized := getTestKey() + line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized) + + authKeys := MarshalAuthorizedKey(pub) + actualFields := strings.Fields(string(authKeys)) + if len(actualFields) == 0 { + t.Fatalf("failed authKeys: %v", authKeys) + } + + // drop the comment + expectedFields := strings.Fields(line)[0:2] + + if !reflect.DeepEqual(actualFields, expectedFields) { + t.Errorf("got %v, expected %v", actualFields, expectedFields) + } + + actPub, _, _, _, err := ParseAuthorizedKey([]byte(line)) + if err != nil { + t.Fatalf("cannot parse %v: %v", line, err) + } + if !reflect.DeepEqual(actPub, pub) { + t.Errorf("got %v, expected %v", actPub, pub) + } +} + +type authResult struct { + pubKey PublicKey + options []string + comments string + rest string + ok bool +} + +func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) { + rest := authKeys + var values []authResult + for len(rest) > 0 { + var r authResult + var err error + r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest) + r.ok = (err == nil) + t.Log(err) + r.rest = string(rest) + values = append(values, r) + } + + if !reflect.DeepEqual(values, expected) { + t.Errorf("got %#v, expected %#v", values, expected) + } +} + +func TestAuthorizedKeyBasic(t *testing.T) { + pub, pubSerialized := getTestKey() + line := "ssh-rsa " + pubSerialized + " user@host" + testAuthorizedKeys(t, []byte(line), + []authResult{ + {pub, nil, "user@host", "", true}, + }) +} + +func TestAuth(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithOptions := []string{ + `# comments to ignore before any keys...`, + ``, + `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`, + `# comments to ignore, along with a blank line`, + ``, + `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`, + ``, + `# more comments, plus a invalid entry`, + `ssh-rsa data-that-will-not-parse user@host3`, + } + for _, eol := range []string{"\n", "\r\n"} { + authOptions := strings.Join(authWithOptions, eol) + rest2 := strings.Join(authWithOptions[3:], eol) + rest3 := strings.Join(authWithOptions[6:], eol) + testAuthorizedKeys(t, []byte(authOptions), []authResult{ + {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true}, + {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true}, + {nil, nil, "", "", false}, + }) + } +} + +func TestAuthWithQuotedSpaceInEnv(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) + testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{ + {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true}, + }) +} + +func TestAuthWithQuotedCommaInEnv(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`) + testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{ + {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true}, + }) +} + +func TestAuthWithQuotedQuoteInEnv(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`) + authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`) + testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{ + {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true}, + }) + + testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{ + {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true}, + }) +} + +func TestAuthWithInvalidSpace(t *testing.T) { + _, pubSerialized := getTestKey() + authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host +#more to follow but still no valid keys`) + testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{ + {nil, nil, "", "", false}, + }) +} + +func TestAuthWithMissingQuote(t *testing.T) { + pub, pubSerialized := getTestKey() + authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host +env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`) + + testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{ + {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true}, + }) +} + +func TestInvalidEntry(t *testing.T) { + authInvalid := []byte(`ssh-rsa`) + _, _, _, _, err := ParseAuthorizedKey(authInvalid) + if err == nil { + t.Errorf("got valid entry for %q", authInvalid) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/mac.go b/vendor/golang.org/x/crypto/ssh/mac.go new file mode 100644 index 0000000..07744ad --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mac.go @@ -0,0 +1,57 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Message authentication support + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "hash" +) + +type macMode struct { + keySize int + new func(key []byte) hash.Hash +} + +// truncatingMAC wraps around a hash.Hash and truncates the output digest to +// a given size. +type truncatingMAC struct { + length int + hmac hash.Hash +} + +func (t truncatingMAC) Write(data []byte) (int, error) { + return t.hmac.Write(data) +} + +func (t truncatingMAC) Sum(in []byte) []byte { + out := t.hmac.Sum(in) + return out[:len(in)+t.length] +} + +func (t truncatingMAC) Reset() { + t.hmac.Reset() +} + +func (t truncatingMAC) Size() int { + return t.length +} + +func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() } + +var macModes = map[string]*macMode{ + "hmac-sha2-256": {32, func(key []byte) hash.Hash { + return hmac.New(sha256.New, key) + }}, + "hmac-sha1": {20, func(key []byte) hash.Hash { + return hmac.New(sha1.New, key) + }}, + "hmac-sha1-96": {20, func(key []byte) hash.Hash { + return truncatingMAC{12, hmac.New(sha1.New, key)} + }}, +} diff --git a/vendor/golang.org/x/crypto/ssh/mempipe_test.go b/vendor/golang.org/x/crypto/ssh/mempipe_test.go new file mode 100644 index 0000000..8697cd6 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mempipe_test.go @@ -0,0 +1,110 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "io" + "sync" + "testing" +) + +// An in-memory packetConn. It is safe to call Close and writePacket +// from different goroutines. +type memTransport struct { + eof bool + pending [][]byte + write *memTransport + sync.Mutex + *sync.Cond +} + +func (t *memTransport) readPacket() ([]byte, error) { + t.Lock() + defer t.Unlock() + for { + if len(t.pending) > 0 { + r := t.pending[0] + t.pending = t.pending[1:] + return r, nil + } + if t.eof { + return nil, io.EOF + } + t.Cond.Wait() + } +} + +func (t *memTransport) closeSelf() error { + t.Lock() + defer t.Unlock() + if t.eof { + return io.EOF + } + t.eof = true + t.Cond.Broadcast() + return nil +} + +func (t *memTransport) Close() error { + err := t.write.closeSelf() + t.closeSelf() + return err +} + +func (t *memTransport) writePacket(p []byte) error { + t.write.Lock() + defer t.write.Unlock() + if t.write.eof { + return io.EOF + } + c := make([]byte, len(p)) + copy(c, p) + t.write.pending = append(t.write.pending, c) + t.write.Cond.Signal() + return nil +} + +func memPipe() (a, b packetConn) { + t1 := memTransport{} + t2 := memTransport{} + t1.write = &t2 + t2.write = &t1 + t1.Cond = sync.NewCond(&t1.Mutex) + t2.Cond = sync.NewCond(&t2.Mutex) + return &t1, &t2 +} + +func TestMemPipe(t *testing.T) { + a, b := memPipe() + if err := a.writePacket([]byte{42}); err != nil { + t.Fatalf("writePacket: %v", err) + } + if err := a.Close(); err != nil { + t.Fatal("Close: ", err) + } + p, err := b.readPacket() + if err != nil { + t.Fatal("readPacket: ", err) + } + if len(p) != 1 || p[0] != 42 { + t.Fatalf("got %v, want {42}", p) + } + p, err = b.readPacket() + if err != io.EOF { + t.Fatalf("got %v, %v, want EOF", p, err) + } +} + +func TestDoubleClose(t *testing.T) { + a, _ := memPipe() + err := a.Close() + if err != nil { + t.Errorf("Close: %v", err) + } + err = a.Close() + if err != io.EOF { + t.Errorf("expect EOF on double close.") + } +} diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go new file mode 100644 index 0000000..eaf6106 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -0,0 +1,725 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "reflect" + "strconv" +) + +// These are SSH message type numbers. They are scattered around several +// documents but many were taken from [SSH-PARAMETERS]. +const ( + msgIgnore = 2 + msgUnimplemented = 3 + msgDebug = 4 + msgNewKeys = 21 + + // Standard authentication messages + msgUserAuthSuccess = 52 + msgUserAuthBanner = 53 +) + +// SSH messages: +// +// These structures mirror the wire format of the corresponding SSH messages. +// They are marshaled using reflection with the marshal and unmarshal functions +// in this file. The only wrinkle is that a final member of type []byte with a +// ssh tag of "rest" receives the remainder of a packet when unmarshaling. + +// See RFC 4253, section 11.1. +const msgDisconnect = 1 + +// disconnectMsg is the message that signals a disconnect. It is also +// the error type returned from mux.Wait() +type disconnectMsg struct { + Reason uint32 `sshtype:"1"` + Message string + Language string +} + +func (d *disconnectMsg) Error() string { + return fmt.Sprintf("ssh: disconnect reason %d: %s", d.Reason, d.Message) +} + +// See RFC 4253, section 7.1. +const msgKexInit = 20 + +type kexInitMsg struct { + Cookie [16]byte `sshtype:"20"` + KexAlgos []string + ServerHostKeyAlgos []string + CiphersClientServer []string + CiphersServerClient []string + MACsClientServer []string + MACsServerClient []string + CompressionClientServer []string + CompressionServerClient []string + LanguagesClientServer []string + LanguagesServerClient []string + FirstKexFollows bool + Reserved uint32 +} + +// See RFC 4253, section 8. + +// Diffie-Helman +const msgKexDHInit = 30 + +type kexDHInitMsg struct { + X *big.Int `sshtype:"30"` +} + +const msgKexECDHInit = 30 + +type kexECDHInitMsg struct { + ClientPubKey []byte `sshtype:"30"` +} + +const msgKexECDHReply = 31 + +type kexECDHReplyMsg struct { + HostKey []byte `sshtype:"31"` + EphemeralPubKey []byte + Signature []byte +} + +const msgKexDHReply = 31 + +type kexDHReplyMsg struct { + HostKey []byte `sshtype:"31"` + Y *big.Int + Signature []byte +} + +// See RFC 4253, section 10. +const msgServiceRequest = 5 + +type serviceRequestMsg struct { + Service string `sshtype:"5"` +} + +// See RFC 4253, section 10. +const msgServiceAccept = 6 + +type serviceAcceptMsg struct { + Service string `sshtype:"6"` +} + +// See RFC 4252, section 5. +const msgUserAuthRequest = 50 + +type userAuthRequestMsg struct { + User string `sshtype:"50"` + Service string + Method string + Payload []byte `ssh:"rest"` +} + +// See RFC 4252, section 5.1 +const msgUserAuthFailure = 51 + +type userAuthFailureMsg struct { + Methods []string `sshtype:"51"` + PartialSuccess bool +} + +// See RFC 4256, section 3.2 +const msgUserAuthInfoRequest = 60 +const msgUserAuthInfoResponse = 61 + +type userAuthInfoRequestMsg struct { + User string `sshtype:"60"` + Instruction string + DeprecatedLanguage string + NumPrompts uint32 + Prompts []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpen = 90 + +type channelOpenMsg struct { + ChanType string `sshtype:"90"` + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +const msgChannelExtendedData = 95 +const msgChannelData = 94 + +// See RFC 4254, section 5.1. +const msgChannelOpenConfirm = 91 + +type channelOpenConfirmMsg struct { + PeersId uint32 `sshtype:"91"` + MyId uint32 + MyWindow uint32 + MaxPacketSize uint32 + TypeSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.1. +const msgChannelOpenFailure = 92 + +type channelOpenFailureMsg struct { + PeersId uint32 `sshtype:"92"` + Reason RejectionReason + Message string + Language string +} + +const msgChannelRequest = 98 + +type channelRequestMsg struct { + PeersId uint32 `sshtype:"98"` + Request string + WantReply bool + RequestSpecificData []byte `ssh:"rest"` +} + +// See RFC 4254, section 5.4. +const msgChannelSuccess = 99 + +type channelRequestSuccessMsg struct { + PeersId uint32 `sshtype:"99"` +} + +// See RFC 4254, section 5.4. +const msgChannelFailure = 100 + +type channelRequestFailureMsg struct { + PeersId uint32 `sshtype:"100"` +} + +// See RFC 4254, section 5.3 +const msgChannelClose = 97 + +type channelCloseMsg struct { + PeersId uint32 `sshtype:"97"` +} + +// See RFC 4254, section 5.3 +const msgChannelEOF = 96 + +type channelEOFMsg struct { + PeersId uint32 `sshtype:"96"` +} + +// See RFC 4254, section 4 +const msgGlobalRequest = 80 + +type globalRequestMsg struct { + Type string `sshtype:"80"` + WantReply bool + Data []byte `ssh:"rest"` +} + +// See RFC 4254, section 4 +const msgRequestSuccess = 81 + +type globalRequestSuccessMsg struct { + Data []byte `ssh:"rest" sshtype:"81"` +} + +// See RFC 4254, section 4 +const msgRequestFailure = 82 + +type globalRequestFailureMsg struct { + Data []byte `ssh:"rest" sshtype:"82"` +} + +// See RFC 4254, section 5.2 +const msgChannelWindowAdjust = 93 + +type windowAdjustMsg struct { + PeersId uint32 `sshtype:"93"` + AdditionalBytes uint32 +} + +// See RFC 4252, section 7 +const msgUserAuthPubKeyOk = 60 + +type userAuthPubKeyOkMsg struct { + Algo string `sshtype:"60"` + PubKey []byte +} + +// typeTag returns the type byte for the given type. The type should +// be struct. +func typeTag(structType reflect.Type) byte { + var tag byte + var tagStr string + tagStr = structType.Field(0).Tag.Get("sshtype") + i, err := strconv.Atoi(tagStr) + if err == nil { + tag = byte(i) + } + return tag +} + +func fieldError(t reflect.Type, field int, problem string) error { + if problem != "" { + problem = ": " + problem + } + return fmt.Errorf("ssh: unmarshal error for field %s of type %s%s", t.Field(field).Name, t.Name(), problem) +} + +var errShortRead = errors.New("ssh: short read") + +// Unmarshal parses data in SSH wire format into a structure. The out +// argument should be a pointer to struct. If the first member of the +// struct has the "sshtype" tag set to a number in decimal, the packet +// must start that number. In case of error, Unmarshal returns a +// ParseError or UnexpectedMessageError. +func Unmarshal(data []byte, out interface{}) error { + v := reflect.ValueOf(out).Elem() + structType := v.Type() + expectedType := typeTag(structType) + if len(data) == 0 { + return parseError(expectedType) + } + if expectedType > 0 { + if data[0] != expectedType { + return unexpectedMessageError(expectedType, data[0]) + } + data = data[1:] + } + + var ok bool + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + t := field.Type() + switch t.Kind() { + case reflect.Bool: + if len(data) < 1 { + return errShortRead + } + field.SetBool(data[0] != 0) + data = data[1:] + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + return fieldError(structType, i, "array of unsupported type") + } + if len(data) < t.Len() { + return errShortRead + } + for j, n := 0, t.Len(); j < n; j++ { + field.Index(j).Set(reflect.ValueOf(data[j])) + } + data = data[t.Len():] + case reflect.Uint64: + var u64 uint64 + if u64, data, ok = parseUint64(data); !ok { + return errShortRead + } + field.SetUint(u64) + case reflect.Uint32: + var u32 uint32 + if u32, data, ok = parseUint32(data); !ok { + return errShortRead + } + field.SetUint(uint64(u32)) + case reflect.Uint8: + if len(data) < 1 { + return errShortRead + } + field.SetUint(uint64(data[0])) + data = data[1:] + case reflect.String: + var s []byte + if s, data, ok = parseString(data); !ok { + return fieldError(structType, i, "") + } + field.SetString(string(s)) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if structType.Field(i).Tag.Get("ssh") == "rest" { + field.Set(reflect.ValueOf(data)) + data = nil + } else { + var s []byte + if s, data, ok = parseString(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(s)) + } + case reflect.String: + var nl []string + if nl, data, ok = parseNameList(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(nl)) + default: + return fieldError(structType, i, "slice of unsupported type") + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + if n, data, ok = parseInt(data); !ok { + return errShortRead + } + field.Set(reflect.ValueOf(n)) + } else { + return fieldError(structType, i, "pointer to unsupported type") + } + default: + return fieldError(structType, i, "unsupported type") + } + } + + if len(data) != 0 { + return parseError(expectedType) + } + + return nil +} + +// Marshal serializes the message in msg to SSH wire format. The msg +// argument should be a struct or pointer to struct. If the first +// member has the "sshtype" tag set to a number in decimal, that +// number is prepended to the result. If the last of member has the +// "ssh" tag set to "rest", its contents are appended to the output. +func Marshal(msg interface{}) []byte { + out := make([]byte, 0, 64) + return marshalStruct(out, msg) +} + +func marshalStruct(out []byte, msg interface{}) []byte { + v := reflect.Indirect(reflect.ValueOf(msg)) + msgType := typeTag(v.Type()) + if msgType > 0 { + out = append(out, msgType) + } + + for i, n := 0, v.NumField(); i < n; i++ { + field := v.Field(i) + switch t := field.Type(); t.Kind() { + case reflect.Bool: + var v uint8 + if field.Bool() { + v = 1 + } + out = append(out, v) + case reflect.Array: + if t.Elem().Kind() != reflect.Uint8 { + panic(fmt.Sprintf("array of non-uint8 in field %d: %T", i, field.Interface())) + } + for j, l := 0, t.Len(); j < l; j++ { + out = append(out, uint8(field.Index(j).Uint())) + } + case reflect.Uint32: + out = appendU32(out, uint32(field.Uint())) + case reflect.Uint64: + out = appendU64(out, uint64(field.Uint())) + case reflect.Uint8: + out = append(out, uint8(field.Uint())) + case reflect.String: + s := field.String() + out = appendInt(out, len(s)) + out = append(out, s...) + case reflect.Slice: + switch t.Elem().Kind() { + case reflect.Uint8: + if v.Type().Field(i).Tag.Get("ssh") != "rest" { + out = appendInt(out, field.Len()) + } + out = append(out, field.Bytes()...) + case reflect.String: + offset := len(out) + out = appendU32(out, 0) + if n := field.Len(); n > 0 { + for j := 0; j < n; j++ { + f := field.Index(j) + if j != 0 { + out = append(out, ',') + } + out = append(out, f.String()...) + } + // overwrite length value + binary.BigEndian.PutUint32(out[offset:], uint32(len(out)-offset-4)) + } + default: + panic(fmt.Sprintf("slice of unknown type in field %d: %T", i, field.Interface())) + } + case reflect.Ptr: + if t == bigIntType { + var n *big.Int + nValue := reflect.ValueOf(&n) + nValue.Elem().Set(field) + needed := intLength(n) + oldLength := len(out) + + if cap(out)-len(out) < needed { + newOut := make([]byte, len(out), 2*(len(out)+needed)) + copy(newOut, out) + out = newOut + } + out = out[:oldLength+needed] + marshalInt(out[oldLength:], n) + } else { + panic(fmt.Sprintf("pointer to unknown type in field %d: %T", i, field.Interface())) + } + } + } + + return out +} + +var bigOne = big.NewInt(1) + +func parseString(in []byte) (out, rest []byte, ok bool) { + if len(in) < 4 { + return + } + length := binary.BigEndian.Uint32(in) + in = in[4:] + if uint32(len(in)) < length { + return + } + out = in[:length] + rest = in[length:] + ok = true + return +} + +var ( + comma = []byte{','} + emptyNameList = []string{} +) + +func parseNameList(in []byte) (out []string, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + if len(contents) == 0 { + out = emptyNameList + return + } + parts := bytes.Split(contents, comma) + out = make([]string, len(parts)) + for i, part := range parts { + out[i] = string(part) + } + return +} + +func parseInt(in []byte) (out *big.Int, rest []byte, ok bool) { + contents, rest, ok := parseString(in) + if !ok { + return + } + out = new(big.Int) + + if len(contents) > 0 && contents[0]&0x80 == 0x80 { + // This is a negative number + notBytes := make([]byte, len(contents)) + for i := range notBytes { + notBytes[i] = ^contents[i] + } + out.SetBytes(notBytes) + out.Add(out, bigOne) + out.Neg(out) + } else { + // Positive number + out.SetBytes(contents) + } + ok = true + return +} + +func parseUint32(in []byte) (uint32, []byte, bool) { + if len(in) < 4 { + return 0, nil, false + } + return binary.BigEndian.Uint32(in), in[4:], true +} + +func parseUint64(in []byte) (uint64, []byte, bool) { + if len(in) < 8 { + return 0, nil, false + } + return binary.BigEndian.Uint64(in), in[8:], true +} + +func intLength(n *big.Int) int { + length := 4 /* length bytes */ + if n.Sign() < 0 { + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bitLen := nMinus1.BitLen() + if bitLen%8 == 0 { + // The number will need 0xff padding + length++ + } + length += (bitLen + 7) / 8 + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bitLen := n.BitLen() + if bitLen%8 == 0 { + // The number will need 0x00 padding + length++ + } + length += (bitLen + 7) / 8 + } + + return length +} + +func marshalUint32(to []byte, n uint32) []byte { + binary.BigEndian.PutUint32(to, n) + return to[4:] +} + +func marshalUint64(to []byte, n uint64) []byte { + binary.BigEndian.PutUint64(to, n) + return to[8:] +} + +func marshalInt(to []byte, n *big.Int) []byte { + lengthBytes := to + to = to[4:] + length := 0 + + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement + // form. So we'll subtract 1 and invert. If the + // most-significant-bit isn't set then we'll need to pad the + // beginning with 0xff in order to keep the number negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + to[0] = 0xff + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } else if n.Sign() == 0 { + // A zero is the zero length string + } else { + bytes := n.Bytes() + if len(bytes) > 0 && bytes[0]&0x80 != 0 { + // We'll have to pad this with a 0x00 in order to + // stop it looking like a negative number. + to[0] = 0 + to = to[1:] + length++ + } + nBytes := copy(to, bytes) + to = to[nBytes:] + length += nBytes + } + + lengthBytes[0] = byte(length >> 24) + lengthBytes[1] = byte(length >> 16) + lengthBytes[2] = byte(length >> 8) + lengthBytes[3] = byte(length) + return to +} + +func writeInt(w io.Writer, n *big.Int) { + length := intLength(n) + buf := make([]byte, length) + marshalInt(buf, n) + w.Write(buf) +} + +func writeString(w io.Writer, s []byte) { + var lengthBytes [4]byte + lengthBytes[0] = byte(len(s) >> 24) + lengthBytes[1] = byte(len(s) >> 16) + lengthBytes[2] = byte(len(s) >> 8) + lengthBytes[3] = byte(len(s)) + w.Write(lengthBytes[:]) + w.Write(s) +} + +func stringLength(n int) int { + return 4 + n +} + +func marshalString(to []byte, s []byte) []byte { + to[0] = byte(len(s) >> 24) + to[1] = byte(len(s) >> 16) + to[2] = byte(len(s) >> 8) + to[3] = byte(len(s)) + to = to[4:] + copy(to, s) + return to[len(s):] +} + +var bigIntType = reflect.TypeOf((*big.Int)(nil)) + +// Decode a packet into its corresponding message. +func decode(packet []byte) (interface{}, error) { + var msg interface{} + switch packet[0] { + case msgDisconnect: + msg = new(disconnectMsg) + case msgServiceRequest: + msg = new(serviceRequestMsg) + case msgServiceAccept: + msg = new(serviceAcceptMsg) + case msgKexInit: + msg = new(kexInitMsg) + case msgKexDHInit: + msg = new(kexDHInitMsg) + case msgKexDHReply: + msg = new(kexDHReplyMsg) + case msgUserAuthRequest: + msg = new(userAuthRequestMsg) + case msgUserAuthFailure: + msg = new(userAuthFailureMsg) + case msgUserAuthPubKeyOk: + msg = new(userAuthPubKeyOkMsg) + case msgGlobalRequest: + msg = new(globalRequestMsg) + case msgRequestSuccess: + msg = new(globalRequestSuccessMsg) + case msgRequestFailure: + msg = new(globalRequestFailureMsg) + case msgChannelOpen: + msg = new(channelOpenMsg) + case msgChannelOpenConfirm: + msg = new(channelOpenConfirmMsg) + case msgChannelOpenFailure: + msg = new(channelOpenFailureMsg) + case msgChannelWindowAdjust: + msg = new(windowAdjustMsg) + case msgChannelEOF: + msg = new(channelEOFMsg) + case msgChannelClose: + msg = new(channelCloseMsg) + case msgChannelRequest: + msg = new(channelRequestMsg) + case msgChannelSuccess: + msg = new(channelRequestSuccessMsg) + case msgChannelFailure: + msg = new(channelRequestFailureMsg) + default: + return nil, unexpectedMessageError(0, packet[0]) + } + if err := Unmarshal(packet, msg); err != nil { + return nil, err + } + return msg, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/messages_test.go b/vendor/golang.org/x/crypto/ssh/messages_test.go new file mode 100644 index 0000000..955b512 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/messages_test.go @@ -0,0 +1,254 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "math/big" + "math/rand" + "reflect" + "testing" + "testing/quick" +) + +var intLengthTests = []struct { + val, length int +}{ + {0, 4 + 0}, + {1, 4 + 1}, + {127, 4 + 1}, + {128, 4 + 2}, + {-1, 4 + 1}, +} + +func TestIntLength(t *testing.T) { + for _, test := range intLengthTests { + v := new(big.Int).SetInt64(int64(test.val)) + length := intLength(v) + if length != test.length { + t.Errorf("For %d, got length %d but expected %d", test.val, length, test.length) + } + } +} + +type msgAllTypes struct { + Bool bool `sshtype:"21"` + Array [16]byte + Uint64 uint64 + Uint32 uint32 + Uint8 uint8 + String string + Strings []string + Bytes []byte + Int *big.Int + Rest []byte `ssh:"rest"` +} + +func (t *msgAllTypes) Generate(rand *rand.Rand, size int) reflect.Value { + m := &msgAllTypes{} + m.Bool = rand.Intn(2) == 1 + randomBytes(m.Array[:], rand) + m.Uint64 = uint64(rand.Int63n(1<<63 - 1)) + m.Uint32 = uint32(rand.Intn((1 << 31) - 1)) + m.Uint8 = uint8(rand.Intn(1 << 8)) + m.String = string(m.Array[:]) + m.Strings = randomNameList(rand) + m.Bytes = m.Array[:] + m.Int = randomInt(rand) + m.Rest = m.Array[:] + return reflect.ValueOf(m) +} + +func TestMarshalUnmarshal(t *testing.T) { + rand := rand.New(rand.NewSource(0)) + iface := &msgAllTypes{} + ty := reflect.ValueOf(iface).Type() + + n := 100 + if testing.Short() { + n = 5 + } + for j := 0; j < n; j++ { + v, ok := quick.Value(ty, rand) + if !ok { + t.Errorf("failed to create value") + break + } + + m1 := v.Elem().Interface() + m2 := iface + + marshaled := Marshal(m1) + if err := Unmarshal(marshaled, m2); err != nil { + t.Errorf("Unmarshal %#v: %s", m1, err) + break + } + + if !reflect.DeepEqual(v.Interface(), m2) { + t.Errorf("got: %#v\nwant:%#v\n%x", m2, m1, marshaled) + break + } + } +} + +func TestUnmarshalEmptyPacket(t *testing.T) { + var b []byte + var m channelRequestSuccessMsg + if err := Unmarshal(b, &m); err == nil { + t.Fatalf("unmarshal of empty slice succeeded") + } +} + +func TestUnmarshalUnexpectedPacket(t *testing.T) { + type S struct { + I uint32 `sshtype:"43"` + S string + B bool + } + + s := S{11, "hello", true} + packet := Marshal(s) + packet[0] = 42 + roundtrip := S{} + err := Unmarshal(packet, &roundtrip) + if err == nil { + t.Fatal("expected error, not nil") + } +} + +func TestMarshalPtr(t *testing.T) { + s := struct { + S string + }{"hello"} + + m1 := Marshal(s) + m2 := Marshal(&s) + if !bytes.Equal(m1, m2) { + t.Errorf("got %q, want %q for marshaled pointer", m2, m1) + } +} + +func TestBareMarshalUnmarshal(t *testing.T) { + type S struct { + I uint32 + S string + B bool + } + + s := S{42, "hello", true} + packet := Marshal(s) + roundtrip := S{} + Unmarshal(packet, &roundtrip) + + if !reflect.DeepEqual(s, roundtrip) { + t.Errorf("got %#v, want %#v", roundtrip, s) + } +} + +func TestBareMarshal(t *testing.T) { + type S2 struct { + I uint32 + } + s := S2{42} + packet := Marshal(s) + i, rest, ok := parseUint32(packet) + if len(rest) > 0 || !ok { + t.Errorf("parseInt(%q): parse error", packet) + } + if i != s.I { + t.Errorf("got %d, want %d", i, s.I) + } +} + +func TestUnmarshalShortKexInitPacket(t *testing.T) { + // This used to panic. + // Issue 11348 + packet := []byte{0x14, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xff, 0xff, 0xff} + kim := &kexInitMsg{} + if err := Unmarshal(packet, kim); err == nil { + t.Error("truncated packet unmarshaled without error") + } +} + +func randomBytes(out []byte, rand *rand.Rand) { + for i := 0; i < len(out); i++ { + out[i] = byte(rand.Int31()) + } +} + +func randomNameList(rand *rand.Rand) []string { + ret := make([]string, rand.Int31()&15) + for i := range ret { + s := make([]byte, 1+(rand.Int31()&15)) + for j := range s { + s[j] = 'a' + uint8(rand.Int31()&15) + } + ret[i] = string(s) + } + return ret +} + +func randomInt(rand *rand.Rand) *big.Int { + return new(big.Int).SetInt64(int64(int32(rand.Uint32()))) +} + +func (*kexInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { + ki := &kexInitMsg{} + randomBytes(ki.Cookie[:], rand) + ki.KexAlgos = randomNameList(rand) + ki.ServerHostKeyAlgos = randomNameList(rand) + ki.CiphersClientServer = randomNameList(rand) + ki.CiphersServerClient = randomNameList(rand) + ki.MACsClientServer = randomNameList(rand) + ki.MACsServerClient = randomNameList(rand) + ki.CompressionClientServer = randomNameList(rand) + ki.CompressionServerClient = randomNameList(rand) + ki.LanguagesClientServer = randomNameList(rand) + ki.LanguagesServerClient = randomNameList(rand) + if rand.Int31()&1 == 1 { + ki.FirstKexFollows = true + } + return reflect.ValueOf(ki) +} + +func (*kexDHInitMsg) Generate(rand *rand.Rand, size int) reflect.Value { + dhi := &kexDHInitMsg{} + dhi.X = randomInt(rand) + return reflect.ValueOf(dhi) +} + +var ( + _kexInitMsg = new(kexInitMsg).Generate(rand.New(rand.NewSource(0)), 10).Elem().Interface() + _kexDHInitMsg = new(kexDHInitMsg).Generate(rand.New(rand.NewSource(0)), 10).Elem().Interface() + + _kexInit = Marshal(_kexInitMsg) + _kexDHInit = Marshal(_kexDHInitMsg) +) + +func BenchmarkMarshalKexInitMsg(b *testing.B) { + for i := 0; i < b.N; i++ { + Marshal(_kexInitMsg) + } +} + +func BenchmarkUnmarshalKexInitMsg(b *testing.B) { + m := new(kexInitMsg) + for i := 0; i < b.N; i++ { + Unmarshal(_kexInit, m) + } +} + +func BenchmarkMarshalKexDHInitMsg(b *testing.B) { + for i := 0; i < b.N; i++ { + Marshal(_kexDHInitMsg) + } +} + +func BenchmarkUnmarshalKexDHInitMsg(b *testing.B) { + m := new(kexDHInitMsg) + for i := 0; i < b.N; i++ { + Unmarshal(_kexDHInit, m) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go new file mode 100644 index 0000000..321880a --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mux.go @@ -0,0 +1,356 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "sync" + "sync/atomic" +) + +// debugMux, if set, causes messages in the connection protocol to be +// logged. +const debugMux = false + +// chanList is a thread safe channel list. +type chanList struct { + // protects concurrent access to chans + sync.Mutex + + // chans are indexed by the local id of the channel, which the + // other side should send in the PeersId field. + chans []*channel + + // This is a debugging aid: it offsets all IDs by this + // amount. This helps distinguish otherwise identical + // server/client muxes + offset uint32 +} + +// Assigns a channel ID to the given channel. +func (c *chanList) add(ch *channel) uint32 { + c.Lock() + defer c.Unlock() + for i := range c.chans { + if c.chans[i] == nil { + c.chans[i] = ch + return uint32(i) + c.offset + } + } + c.chans = append(c.chans, ch) + return uint32(len(c.chans)-1) + c.offset +} + +// getChan returns the channel for the given ID. +func (c *chanList) getChan(id uint32) *channel { + id -= c.offset + + c.Lock() + defer c.Unlock() + if id < uint32(len(c.chans)) { + return c.chans[id] + } + return nil +} + +func (c *chanList) remove(id uint32) { + id -= c.offset + c.Lock() + if id < uint32(len(c.chans)) { + c.chans[id] = nil + } + c.Unlock() +} + +// dropAll forgets all channels it knows, returning them in a slice. +func (c *chanList) dropAll() []*channel { + c.Lock() + defer c.Unlock() + var r []*channel + + for _, ch := range c.chans { + if ch == nil { + continue + } + r = append(r, ch) + } + c.chans = nil + return r +} + +// mux represents the state for the SSH connection protocol, which +// multiplexes many channels onto a single packet transport. +type mux struct { + conn packetConn + chanList chanList + + incomingChannels chan NewChannel + + globalSentMu sync.Mutex + globalResponses chan interface{} + incomingRequests chan *Request + + errCond *sync.Cond + err error +} + +// When debugging, each new chanList instantiation has a different +// offset. +var globalOff uint32 + +func (m *mux) Wait() error { + m.errCond.L.Lock() + defer m.errCond.L.Unlock() + for m.err == nil { + m.errCond.Wait() + } + return m.err +} + +// newMux returns a mux that runs over the given connection. +func newMux(p packetConn) *mux { + m := &mux{ + conn: p, + incomingChannels: make(chan NewChannel, 16), + globalResponses: make(chan interface{}, 1), + incomingRequests: make(chan *Request, 16), + errCond: newCond(), + } + if debugMux { + m.chanList.offset = atomic.AddUint32(&globalOff, 1) + } + + go m.loop() + return m +} + +func (m *mux) sendMessage(msg interface{}) error { + p := Marshal(msg) + return m.conn.writePacket(p) +} + +func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) { + if wantReply { + m.globalSentMu.Lock() + defer m.globalSentMu.Unlock() + } + + if err := m.sendMessage(globalRequestMsg{ + Type: name, + WantReply: wantReply, + Data: payload, + }); err != nil { + return false, nil, err + } + + if !wantReply { + return false, nil, nil + } + + msg, ok := <-m.globalResponses + if !ok { + return false, nil, io.EOF + } + switch msg := msg.(type) { + case *globalRequestFailureMsg: + return false, msg.Data, nil + case *globalRequestSuccessMsg: + return true, msg.Data, nil + default: + return false, nil, fmt.Errorf("ssh: unexpected response to request: %#v", msg) + } +} + +// ackRequest must be called after processing a global request that +// has WantReply set. +func (m *mux) ackRequest(ok bool, data []byte) error { + if ok { + return m.sendMessage(globalRequestSuccessMsg{Data: data}) + } + return m.sendMessage(globalRequestFailureMsg{Data: data}) +} + +// TODO(hanwen): Disconnect is a transport layer message. We should +// probably send and receive Disconnect somewhere in the transport +// code. + +// Disconnect sends a disconnect message. +func (m *mux) Disconnect(reason uint32, message string) error { + return m.sendMessage(disconnectMsg{ + Reason: reason, + Message: message, + }) +} + +func (m *mux) Close() error { + return m.conn.Close() +} + +// loop runs the connection machine. It will process packets until an +// error is encountered. To synchronize on loop exit, use mux.Wait. +func (m *mux) loop() { + var err error + for err == nil { + err = m.onePacket() + } + + for _, ch := range m.chanList.dropAll() { + ch.close() + } + + close(m.incomingChannels) + close(m.incomingRequests) + close(m.globalResponses) + + m.conn.Close() + + m.errCond.L.Lock() + m.err = err + m.errCond.Broadcast() + m.errCond.L.Unlock() + + if debugMux { + log.Println("loop exit", err) + } +} + +// onePacket reads and processes one packet. +func (m *mux) onePacket() error { + packet, err := m.conn.readPacket() + if err != nil { + return err + } + + if debugMux { + if packet[0] == msgChannelData || packet[0] == msgChannelExtendedData { + log.Printf("decoding(%d): data packet - %d bytes", m.chanList.offset, len(packet)) + } else { + p, _ := decode(packet) + log.Printf("decoding(%d): %d %#v - %d bytes", m.chanList.offset, packet[0], p, len(packet)) + } + } + + switch packet[0] { + case msgNewKeys: + // Ignore notification of key change. + return nil + case msgDisconnect: + return m.handleDisconnect(packet) + case msgChannelOpen: + return m.handleChannelOpen(packet) + case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: + return m.handleGlobalPacket(packet) + } + + // assume a channel packet. + if len(packet) < 5 { + return parseError(packet[0]) + } + id := binary.BigEndian.Uint32(packet[1:]) + ch := m.chanList.getChan(id) + if ch == nil { + return fmt.Errorf("ssh: invalid channel %d", id) + } + + return ch.handlePacket(packet) +} + +func (m *mux) handleDisconnect(packet []byte) error { + var d disconnectMsg + if err := Unmarshal(packet, &d); err != nil { + return err + } + + if debugMux { + log.Printf("caught disconnect: %v", d) + } + return &d +} + +func (m *mux) handleGlobalPacket(packet []byte) error { + msg, err := decode(packet) + if err != nil { + return err + } + + switch msg := msg.(type) { + case *globalRequestMsg: + m.incomingRequests <- &Request{ + Type: msg.Type, + WantReply: msg.WantReply, + Payload: msg.Data, + mux: m, + } + case *globalRequestSuccessMsg, *globalRequestFailureMsg: + m.globalResponses <- msg + default: + panic(fmt.Sprintf("not a global message %#v", msg)) + } + + return nil +} + +// handleChannelOpen schedules a channel to be Accept()ed. +func (m *mux) handleChannelOpen(packet []byte) error { + var msg channelOpenMsg + if err := Unmarshal(packet, &msg); err != nil { + return err + } + + if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { + failMsg := channelOpenFailureMsg{ + PeersId: msg.PeersId, + Reason: ConnectionFailed, + Message: "invalid request", + Language: "en_US.UTF-8", + } + return m.sendMessage(failMsg) + } + + c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData) + c.remoteId = msg.PeersId + c.maxRemotePayload = msg.MaxPacketSize + c.remoteWin.add(msg.PeersWindow) + m.incomingChannels <- c + return nil +} + +func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Request, error) { + ch, err := m.openChannel(chanType, extra) + if err != nil { + return nil, nil, err + } + + return ch, ch.incomingRequests, nil +} + +func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) { + ch := m.newChannel(chanType, channelOutbound, extra) + + ch.maxIncomingPayload = channelMaxPacket + + open := channelOpenMsg{ + ChanType: chanType, + PeersWindow: ch.myWindow, + MaxPacketSize: ch.maxIncomingPayload, + TypeSpecificData: extra, + PeersId: ch.localId, + } + if err := m.sendMessage(open); err != nil { + return nil, err + } + + switch msg := (<-ch.msg).(type) { + case *channelOpenConfirmMsg: + return ch, nil + case *channelOpenFailureMsg: + return nil, &OpenChannelError{msg.Reason, msg.Message} + default: + return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/mux_test.go b/vendor/golang.org/x/crypto/ssh/mux_test.go new file mode 100644 index 0000000..5230389 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/mux_test.go @@ -0,0 +1,525 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "io" + "io/ioutil" + "sync" + "testing" +) + +func muxPair() (*mux, *mux) { + a, b := memPipe() + + s := newMux(a) + c := newMux(b) + + return s, c +} + +// Returns both ends of a channel, and the mux for the the 2nd +// channel. +func channelPair(t *testing.T) (*channel, *channel, *mux) { + c, s := muxPair() + + res := make(chan *channel, 1) + go func() { + newCh, ok := <-s.incomingChannels + if !ok { + t.Fatalf("No incoming channel") + } + if newCh.ChannelType() != "chan" { + t.Fatalf("got type %q want chan", newCh.ChannelType()) + } + ch, _, err := newCh.Accept() + if err != nil { + t.Fatalf("Accept %v", err) + } + res <- ch.(*channel) + }() + + ch, err := c.openChannel("chan", nil) + if err != nil { + t.Fatalf("OpenChannel: %v", err) + } + + return <-res, ch, c +} + +// Test that stderr and stdout can be addressed from different +// goroutines. This is intended for use with the race detector. +func TestMuxChannelExtendedThreadSafety(t *testing.T) { + writer, reader, mux := channelPair(t) + defer writer.Close() + defer reader.Close() + defer mux.Close() + + var wr, rd sync.WaitGroup + magic := "hello world" + + wr.Add(2) + go func() { + io.WriteString(writer, magic) + wr.Done() + }() + go func() { + io.WriteString(writer.Stderr(), magic) + wr.Done() + }() + + rd.Add(2) + go func() { + c, err := ioutil.ReadAll(reader) + if string(c) != magic { + t.Fatalf("stdout read got %q, want %q (error %s)", c, magic, err) + } + rd.Done() + }() + go func() { + c, err := ioutil.ReadAll(reader.Stderr()) + if string(c) != magic { + t.Fatalf("stderr read got %q, want %q (error %s)", c, magic, err) + } + rd.Done() + }() + + wr.Wait() + writer.CloseWrite() + rd.Wait() +} + +func TestMuxReadWrite(t *testing.T) { + s, c, mux := channelPair(t) + defer s.Close() + defer c.Close() + defer mux.Close() + + magic := "hello world" + magicExt := "hello stderr" + go func() { + _, err := s.Write([]byte(magic)) + if err != nil { + t.Fatalf("Write: %v", err) + } + _, err = s.Extended(1).Write([]byte(magicExt)) + if err != nil { + t.Fatalf("Write: %v", err) + } + err = s.Close() + if err != nil { + t.Fatalf("Close: %v", err) + } + }() + + var buf [1024]byte + n, err := c.Read(buf[:]) + if err != nil { + t.Fatalf("server Read: %v", err) + } + got := string(buf[:n]) + if got != magic { + t.Fatalf("server: got %q want %q", got, magic) + } + + n, err = c.Extended(1).Read(buf[:]) + if err != nil { + t.Fatalf("server Read: %v", err) + } + + got = string(buf[:n]) + if got != magicExt { + t.Fatalf("server: got %q want %q", got, magic) + } +} + +func TestMuxChannelOverflow(t *testing.T) { + reader, writer, mux := channelPair(t) + defer reader.Close() + defer writer.Close() + defer mux.Close() + + wDone := make(chan int, 1) + go func() { + if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil { + t.Errorf("could not fill window: %v", err) + } + writer.Write(make([]byte, 1)) + wDone <- 1 + }() + writer.remoteWin.waitWriterBlocked() + + // Send 1 byte. + packet := make([]byte, 1+4+4+1) + packet[0] = msgChannelData + marshalUint32(packet[1:], writer.remoteId) + marshalUint32(packet[5:], uint32(1)) + packet[9] = 42 + + if err := writer.mux.conn.writePacket(packet); err != nil { + t.Errorf("could not send packet") + } + if _, err := reader.SendRequest("hello", true, nil); err == nil { + t.Errorf("SendRequest succeeded.") + } + <-wDone +} + +func TestMuxChannelCloseWriteUnblock(t *testing.T) { + reader, writer, mux := channelPair(t) + defer reader.Close() + defer writer.Close() + defer mux.Close() + + wDone := make(chan int, 1) + go func() { + if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil { + t.Errorf("could not fill window: %v", err) + } + if _, err := writer.Write(make([]byte, 1)); err != io.EOF { + t.Errorf("got %v, want EOF for unblock write", err) + } + wDone <- 1 + }() + + writer.remoteWin.waitWriterBlocked() + reader.Close() + <-wDone +} + +func TestMuxConnectionCloseWriteUnblock(t *testing.T) { + reader, writer, mux := channelPair(t) + defer reader.Close() + defer writer.Close() + defer mux.Close() + + wDone := make(chan int, 1) + go func() { + if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil { + t.Errorf("could not fill window: %v", err) + } + if _, err := writer.Write(make([]byte, 1)); err != io.EOF { + t.Errorf("got %v, want EOF for unblock write", err) + } + wDone <- 1 + }() + + writer.remoteWin.waitWriterBlocked() + mux.Close() + <-wDone +} + +func TestMuxReject(t *testing.T) { + client, server := muxPair() + defer server.Close() + defer client.Close() + + go func() { + ch, ok := <-server.incomingChannels + if !ok { + t.Fatalf("Accept") + } + if ch.ChannelType() != "ch" || string(ch.ExtraData()) != "extra" { + t.Fatalf("unexpected channel: %q, %q", ch.ChannelType(), ch.ExtraData()) + } + ch.Reject(RejectionReason(42), "message") + }() + + ch, err := client.openChannel("ch", []byte("extra")) + if ch != nil { + t.Fatal("openChannel not rejected") + } + + ocf, ok := err.(*OpenChannelError) + if !ok { + t.Errorf("got %#v want *OpenChannelError", err) + } else if ocf.Reason != 42 || ocf.Message != "message" { + t.Errorf("got %#v, want {Reason: 42, Message: %q}", ocf, "message") + } + + want := "ssh: rejected: unknown reason 42 (message)" + if err.Error() != want { + t.Errorf("got %q, want %q", err.Error(), want) + } +} + +func TestMuxChannelRequest(t *testing.T) { + client, server, mux := channelPair(t) + defer server.Close() + defer client.Close() + defer mux.Close() + + var received int + var wg sync.WaitGroup + wg.Add(1) + go func() { + for r := range server.incomingRequests { + received++ + r.Reply(r.Type == "yes", nil) + } + wg.Done() + }() + _, err := client.SendRequest("yes", false, nil) + if err != nil { + t.Fatalf("SendRequest: %v", err) + } + ok, err := client.SendRequest("yes", true, nil) + if err != nil { + t.Fatalf("SendRequest: %v", err) + } + + if !ok { + t.Errorf("SendRequest(yes): %v", ok) + + } + + ok, err = client.SendRequest("no", true, nil) + if err != nil { + t.Fatalf("SendRequest: %v", err) + } + if ok { + t.Errorf("SendRequest(no): %v", ok) + + } + + client.Close() + wg.Wait() + + if received != 3 { + t.Errorf("got %d requests, want %d", received, 3) + } +} + +func TestMuxGlobalRequest(t *testing.T) { + clientMux, serverMux := muxPair() + defer serverMux.Close() + defer clientMux.Close() + + var seen bool + go func() { + for r := range serverMux.incomingRequests { + seen = seen || r.Type == "peek" + if r.WantReply { + err := r.Reply(r.Type == "yes", + append([]byte(r.Type), r.Payload...)) + if err != nil { + t.Errorf("AckRequest: %v", err) + } + } + } + }() + + _, _, err := clientMux.SendRequest("peek", false, nil) + if err != nil { + t.Errorf("SendRequest: %v", err) + } + + ok, data, err := clientMux.SendRequest("yes", true, []byte("a")) + if !ok || string(data) != "yesa" || err != nil { + t.Errorf("SendRequest(\"yes\", true, \"a\"): %v %v %v", + ok, data, err) + } + if ok, data, err := clientMux.SendRequest("yes", true, []byte("a")); !ok || string(data) != "yesa" || err != nil { + t.Errorf("SendRequest(\"yes\", true, \"a\"): %v %v %v", + ok, data, err) + } + + if ok, data, err := clientMux.SendRequest("no", true, []byte("a")); ok || string(data) != "noa" || err != nil { + t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v", + ok, data, err) + } + + clientMux.Disconnect(0, "") + if !seen { + t.Errorf("never saw 'peek' request") + } +} + +func TestMuxGlobalRequestUnblock(t *testing.T) { + clientMux, serverMux := muxPair() + defer serverMux.Close() + defer clientMux.Close() + + result := make(chan error, 1) + go func() { + _, _, err := clientMux.SendRequest("hello", true, nil) + result <- err + }() + + <-serverMux.incomingRequests + serverMux.conn.Close() + err := <-result + + if err != io.EOF { + t.Errorf("want EOF, got %v", io.EOF) + } +} + +func TestMuxChannelRequestUnblock(t *testing.T) { + a, b, connB := channelPair(t) + defer a.Close() + defer b.Close() + defer connB.Close() + + result := make(chan error, 1) + go func() { + _, err := a.SendRequest("hello", true, nil) + result <- err + }() + + <-b.incomingRequests + connB.conn.Close() + err := <-result + + if err != io.EOF { + t.Errorf("want EOF, got %v", err) + } +} + +func TestMuxDisconnect(t *testing.T) { + a, b := muxPair() + defer a.Close() + defer b.Close() + + go func() { + for r := range b.incomingRequests { + r.Reply(true, nil) + } + }() + + a.Disconnect(42, "whatever") + ok, _, err := a.SendRequest("hello", true, nil) + if ok || err == nil { + t.Errorf("got reply after disconnecting") + } + err = b.Wait() + if d, ok := err.(*disconnectMsg); !ok || d.Reason != 42 { + t.Errorf("got %#v, want disconnectMsg{Reason:42}", err) + } +} + +func TestMuxCloseChannel(t *testing.T) { + r, w, mux := channelPair(t) + defer mux.Close() + defer r.Close() + defer w.Close() + + result := make(chan error, 1) + go func() { + var b [1024]byte + _, err := r.Read(b[:]) + result <- err + }() + if err := w.Close(); err != nil { + t.Errorf("w.Close: %v", err) + } + + if _, err := w.Write([]byte("hello")); err != io.EOF { + t.Errorf("got err %v, want io.EOF after Close", err) + } + + if err := <-result; err != io.EOF { + t.Errorf("got %v (%T), want io.EOF", err, err) + } +} + +func TestMuxCloseWriteChannel(t *testing.T) { + r, w, mux := channelPair(t) + defer mux.Close() + + result := make(chan error, 1) + go func() { + var b [1024]byte + _, err := r.Read(b[:]) + result <- err + }() + if err := w.CloseWrite(); err != nil { + t.Errorf("w.CloseWrite: %v", err) + } + + if _, err := w.Write([]byte("hello")); err != io.EOF { + t.Errorf("got err %v, want io.EOF after CloseWrite", err) + } + + if err := <-result; err != io.EOF { + t.Errorf("got %v (%T), want io.EOF", err, err) + } +} + +func TestMuxInvalidRecord(t *testing.T) { + a, b := muxPair() + defer a.Close() + defer b.Close() + + packet := make([]byte, 1+4+4+1) + packet[0] = msgChannelData + marshalUint32(packet[1:], 29348723 /* invalid channel id */) + marshalUint32(packet[5:], 1) + packet[9] = 42 + + a.conn.writePacket(packet) + go a.SendRequest("hello", false, nil) + // 'a' wrote an invalid packet, so 'b' has exited. + req, ok := <-b.incomingRequests + if ok { + t.Errorf("got request %#v after receiving invalid packet", req) + } +} + +func TestZeroWindowAdjust(t *testing.T) { + a, b, mux := channelPair(t) + defer a.Close() + defer b.Close() + defer mux.Close() + + go func() { + io.WriteString(a, "hello") + // bogus adjust. + a.sendMessage(windowAdjustMsg{}) + io.WriteString(a, "world") + a.Close() + }() + + want := "helloworld" + c, _ := ioutil.ReadAll(b) + if string(c) != want { + t.Errorf("got %q want %q", c, want) + } +} + +func TestMuxMaxPacketSize(t *testing.T) { + a, b, mux := channelPair(t) + defer a.Close() + defer b.Close() + defer mux.Close() + + large := make([]byte, a.maxRemotePayload+1) + packet := make([]byte, 1+4+4+1+len(large)) + packet[0] = msgChannelData + marshalUint32(packet[1:], a.remoteId) + marshalUint32(packet[5:], uint32(len(large))) + packet[9] = 42 + + if err := a.mux.conn.writePacket(packet); err != nil { + t.Errorf("could not send packet") + } + + go a.SendRequest("hello", false, nil) + + _, ok := <-b.incomingRequests + if ok { + t.Errorf("connection still alive after receiving large packet.") + } +} + +// Don't ship code with debug=true. +func TestDebug(t *testing.T) { + if debugMux { + t.Error("mux debug switched on") + } + if debugHandshake { + t.Error("handshake debug switched on") + } +} diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go new file mode 100644 index 0000000..baedf5b --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -0,0 +1,493 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "errors" + "fmt" + "io" + "net" +) + +// The Permissions type holds fine-grained permissions that are +// specific to a user or a specific authentication method for a +// user. Permissions, except for "source-address", must be enforced in +// the server application layer, after successful authentication. The +// Permissions are passed on in ServerConn so a server implementation +// can honor them. +type Permissions struct { + // Critical options restrict default permissions. Common + // restrictions are "source-address" and "force-command". If + // the server cannot enforce the restriction, or does not + // recognize it, the user should not authenticate. + CriticalOptions map[string]string + + // Extensions are extra functionality that the server may + // offer on authenticated connections. Common extensions are + // "permit-agent-forwarding", "permit-X11-forwarding". Lack of + // support for an extension does not preclude authenticating a + // user. + Extensions map[string]string +} + +// ServerConfig holds server specific configuration data. +type ServerConfig struct { + // Config contains configuration shared between client and server. + Config + + hostKeys []Signer + + // NoClientAuth is true if clients are allowed to connect without + // authenticating. + NoClientAuth bool + + // PasswordCallback, if non-nil, is called when a user + // attempts to authenticate using a password. + PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) + + // PublicKeyCallback, if non-nil, is called when a client attempts public + // key authentication. It must return true if the given public key is + // valid for the given user. For example, see CertChecker.Authenticate. + PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) + + // KeyboardInteractiveCallback, if non-nil, is called when + // keyboard-interactive authentication is selected (RFC + // 4256). The client object's Challenge function should be + // used to query the user. The callback may offer multiple + // Challenge rounds. To avoid information leaks, the client + // should be presented a challenge even if the user is + // unknown. + KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error) + + // AuthLogCallback, if non-nil, is called to log all authentication + // attempts. + AuthLogCallback func(conn ConnMetadata, method string, err error) + + // ServerVersion is the version identification string to + // announce in the public handshake. + // If empty, a reasonable default is used. + ServerVersion string +} + +// AddHostKey adds a private key as a host key. If an existing host +// key exists with the same algorithm, it is overwritten. Each server +// config must have at least one host key. +func (s *ServerConfig) AddHostKey(key Signer) { + for i, k := range s.hostKeys { + if k.PublicKey().Type() == key.PublicKey().Type() { + s.hostKeys[i] = key + return + } + } + + s.hostKeys = append(s.hostKeys, key) +} + +// cachedPubKey contains the results of querying whether a public key is +// acceptable for a user. +type cachedPubKey struct { + user string + pubKeyData []byte + result error + perms *Permissions +} + +const maxCachedPubKeys = 16 + +// pubKeyCache caches tests for public keys. Since SSH clients +// will query whether a public key is acceptable before attempting to +// authenticate with it, we end up with duplicate queries for public +// key validity. The cache only applies to a single ServerConn. +type pubKeyCache struct { + keys []cachedPubKey +} + +// get returns the result for a given user/algo/key tuple. +func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) { + for _, k := range c.keys { + if k.user == user && bytes.Equal(k.pubKeyData, pubKeyData) { + return k, true + } + } + return cachedPubKey{}, false +} + +// add adds the given tuple to the cache. +func (c *pubKeyCache) add(candidate cachedPubKey) { + if len(c.keys) < maxCachedPubKeys { + c.keys = append(c.keys, candidate) + } +} + +// ServerConn is an authenticated SSH connection, as seen from the +// server +type ServerConn struct { + Conn + + // If the succeeding authentication callback returned a + // non-nil Permissions pointer, it is stored here. + Permissions *Permissions +} + +// NewServerConn starts a new SSH server with c as the underlying +// transport. It starts with a handshake and, if the handshake is +// unsuccessful, it closes the connection and returns an error. The +// Request and NewChannel channels must be serviced, or the connection +// will hang. +func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { + fullConf := *config + fullConf.SetDefaults() + s := &connection{ + sshConn: sshConn{conn: c}, + } + perms, err := s.serverHandshake(&fullConf) + if err != nil { + c.Close() + return nil, nil, nil, err + } + return &ServerConn{s, perms}, s.mux.incomingChannels, s.mux.incomingRequests, nil +} + +// signAndMarshal signs the data with the appropriate algorithm, +// and serializes the result in SSH wire format. +func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) { + sig, err := k.Sign(rand, data) + if err != nil { + return nil, err + } + + return Marshal(sig), nil +} + +// handshake performs key exchange and user authentication. +func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) { + if len(config.hostKeys) == 0 { + return nil, errors.New("ssh: server has no host keys") + } + + if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && config.KeyboardInteractiveCallback == nil { + return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if config.ServerVersion != "" { + s.serverVersion = []byte(config.ServerVersion) + } else { + s.serverVersion = []byte(packageVersion) + } + var err error + s.clientVersion, err = exchangeVersions(s.sshConn.conn, s.serverVersion) + if err != nil { + return nil, err + } + + tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) + s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) + + if err := s.transport.requestKeyChange(); err != nil { + return nil, err + } + + if packet, err := s.transport.readPacket(); err != nil { + return nil, err + } else if packet[0] != msgNewKeys { + return nil, unexpectedMessageError(msgNewKeys, packet[0]) + } + + // We just did the key change, so the session ID is established. + s.sessionID = s.transport.getSessionID() + + var packet []byte + if packet, err = s.transport.readPacket(); err != nil { + return nil, err + } + + var serviceRequest serviceRequestMsg + if err = Unmarshal(packet, &serviceRequest); err != nil { + return nil, err + } + if serviceRequest.Service != serviceUserAuth { + return nil, errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating") + } + serviceAccept := serviceAcceptMsg{ + Service: serviceUserAuth, + } + if err := s.transport.writePacket(Marshal(&serviceAccept)); err != nil { + return nil, err + } + + perms, err := s.serverAuthenticate(config) + if err != nil { + return nil, err + } + s.mux = newMux(s.transport) + return perms, err +} + +func isAcceptableAlgo(algo string) bool { + switch algo { + case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, + CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: + return true + } + return false +} + +func checkSourceAddress(addr net.Addr, sourceAddr string) error { + if addr == nil { + return errors.New("ssh: no address known for client, but source-address match required") + } + + tcpAddr, ok := addr.(*net.TCPAddr) + if !ok { + return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) + } + + if allowedIP := net.ParseIP(sourceAddr); allowedIP != nil { + if bytes.Equal(allowedIP, tcpAddr.IP) { + return nil + } + } else { + _, ipNet, err := net.ParseCIDR(sourceAddr) + if err != nil { + return fmt.Errorf("ssh: error parsing source-address restriction %q: %v", sourceAddr, err) + } + + if ipNet.Contains(tcpAddr.IP) { + return nil + } + } + + return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) +} + +func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { + var err error + var cache pubKeyCache + var perms *Permissions + +userAuthLoop: + for { + var userAuthReq userAuthRequestMsg + if packet, err := s.transport.readPacket(); err != nil { + return nil, err + } else if err = Unmarshal(packet, &userAuthReq); err != nil { + return nil, err + } + + if userAuthReq.Service != serviceSSH { + return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) + } + + s.user = userAuthReq.User + perms = nil + authErr := errors.New("no auth passed yet") + + switch userAuthReq.Method { + case "none": + if config.NoClientAuth { + s.user = "" + authErr = nil + } + case "password": + if config.PasswordCallback == nil { + authErr = errors.New("ssh: password auth not configured") + break + } + payload := userAuthReq.Payload + if len(payload) < 1 || payload[0] != 0 { + return nil, parseError(msgUserAuthRequest) + } + payload = payload[1:] + password, payload, ok := parseString(payload) + if !ok || len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + + perms, authErr = config.PasswordCallback(s, password) + case "keyboard-interactive": + if config.KeyboardInteractiveCallback == nil { + authErr = errors.New("ssh: keyboard-interactive auth not configubred") + break + } + + prompter := &sshClientKeyboardInteractive{s} + perms, authErr = config.KeyboardInteractiveCallback(s, prompter.Challenge) + case "publickey": + if config.PublicKeyCallback == nil { + authErr = errors.New("ssh: publickey auth not configured") + break + } + payload := userAuthReq.Payload + if len(payload) < 1 { + return nil, parseError(msgUserAuthRequest) + } + isQuery := payload[0] == 0 + payload = payload[1:] + algoBytes, payload, ok := parseString(payload) + if !ok { + return nil, parseError(msgUserAuthRequest) + } + algo := string(algoBytes) + if !isAcceptableAlgo(algo) { + authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo) + break + } + + pubKeyData, payload, ok := parseString(payload) + if !ok { + return nil, parseError(msgUserAuthRequest) + } + + pubKey, err := ParsePublicKey(pubKeyData) + if err != nil { + return nil, err + } + + candidate, ok := cache.get(s.user, pubKeyData) + if !ok { + candidate.user = s.user + candidate.pubKeyData = pubKeyData + candidate.perms, candidate.result = config.PublicKeyCallback(s, pubKey) + if candidate.result == nil && candidate.perms != nil && candidate.perms.CriticalOptions != nil && candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" { + candidate.result = checkSourceAddress( + s.RemoteAddr(), + candidate.perms.CriticalOptions[sourceAddressCriticalOption]) + } + cache.add(candidate) + } + + if isQuery { + // The client can query if the given public key + // would be okay. + if len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + + if candidate.result == nil { + okMsg := userAuthPubKeyOkMsg{ + Algo: algo, + PubKey: pubKeyData, + } + if err = s.transport.writePacket(Marshal(&okMsg)); err != nil { + return nil, err + } + continue userAuthLoop + } + authErr = candidate.result + } else { + sig, payload, ok := parseSignature(payload) + if !ok || len(payload) > 0 { + return nil, parseError(msgUserAuthRequest) + } + // Ensure the public key algo and signature algo + // are supported. Compare the private key + // algorithm name that corresponds to algo with + // sig.Format. This is usually the same, but + // for certs, the names differ. + if !isAcceptableAlgo(sig.Format) { + break + } + signedData := buildDataSignedForAuth(s.transport.getSessionID(), userAuthReq, algoBytes, pubKeyData) + + if err := pubKey.Verify(signedData, sig); err != nil { + return nil, err + } + + authErr = candidate.result + perms = candidate.perms + } + default: + authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) + } + + if config.AuthLogCallback != nil { + config.AuthLogCallback(s, userAuthReq.Method, authErr) + } + + if authErr == nil { + break userAuthLoop + } + + var failureMsg userAuthFailureMsg + if config.PasswordCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "password") + } + if config.PublicKeyCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "publickey") + } + if config.KeyboardInteractiveCallback != nil { + failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") + } + + if len(failureMsg.Methods) == 0 { + return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") + } + + if err = s.transport.writePacket(Marshal(&failureMsg)); err != nil { + return nil, err + } + } + + if err = s.transport.writePacket([]byte{msgUserAuthSuccess}); err != nil { + return nil, err + } + return perms, nil +} + +// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by +// asking the client on the other side of a ServerConn. +type sshClientKeyboardInteractive struct { + *connection +} + +func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) { + if len(questions) != len(echos) { + return nil, errors.New("ssh: echos and questions must have equal length") + } + + var prompts []byte + for i := range questions { + prompts = appendString(prompts, questions[i]) + prompts = appendBool(prompts, echos[i]) + } + + if err := c.transport.writePacket(Marshal(&userAuthInfoRequestMsg{ + Instruction: instruction, + NumPrompts: uint32(len(questions)), + Prompts: prompts, + })); err != nil { + return nil, err + } + + packet, err := c.transport.readPacket() + if err != nil { + return nil, err + } + if packet[0] != msgUserAuthInfoResponse { + return nil, unexpectedMessageError(msgUserAuthInfoResponse, packet[0]) + } + packet = packet[1:] + + n, packet, ok := parseUint32(packet) + if !ok || int(n) != len(questions) { + return nil, parseError(msgUserAuthInfoResponse) + } + + for i := uint32(0); i < n; i++ { + ans, rest, ok := parseString(packet) + if !ok { + return nil, parseError(msgUserAuthInfoResponse) + } + + answers = append(answers, string(ans)) + packet = rest + } + if len(packet) != 0 { + return nil, errors.New("ssh: junk at end of message") + } + + return answers, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go new file mode 100644 index 0000000..3b42b50 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -0,0 +1,605 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Session implements an interactive session described in +// "RFC 4254, section 6". + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "sync" +) + +type Signal string + +// POSIX signals as listed in RFC 4254 Section 6.10. +const ( + SIGABRT Signal = "ABRT" + SIGALRM Signal = "ALRM" + SIGFPE Signal = "FPE" + SIGHUP Signal = "HUP" + SIGILL Signal = "ILL" + SIGINT Signal = "INT" + SIGKILL Signal = "KILL" + SIGPIPE Signal = "PIPE" + SIGQUIT Signal = "QUIT" + SIGSEGV Signal = "SEGV" + SIGTERM Signal = "TERM" + SIGUSR1 Signal = "USR1" + SIGUSR2 Signal = "USR2" +) + +var signals = map[Signal]int{ + SIGABRT: 6, + SIGALRM: 14, + SIGFPE: 8, + SIGHUP: 1, + SIGILL: 4, + SIGINT: 2, + SIGKILL: 9, + SIGPIPE: 13, + SIGQUIT: 3, + SIGSEGV: 11, + SIGTERM: 15, +} + +type TerminalModes map[uint8]uint32 + +// POSIX terminal mode flags as listed in RFC 4254 Section 8. +const ( + tty_OP_END = 0 + VINTR = 1 + VQUIT = 2 + VERASE = 3 + VKILL = 4 + VEOF = 5 + VEOL = 6 + VEOL2 = 7 + VSTART = 8 + VSTOP = 9 + VSUSP = 10 + VDSUSP = 11 + VREPRINT = 12 + VWERASE = 13 + VLNEXT = 14 + VFLUSH = 15 + VSWTCH = 16 + VSTATUS = 17 + VDISCARD = 18 + IGNPAR = 30 + PARMRK = 31 + INPCK = 32 + ISTRIP = 33 + INLCR = 34 + IGNCR = 35 + ICRNL = 36 + IUCLC = 37 + IXON = 38 + IXANY = 39 + IXOFF = 40 + IMAXBEL = 41 + ISIG = 50 + ICANON = 51 + XCASE = 52 + ECHO = 53 + ECHOE = 54 + ECHOK = 55 + ECHONL = 56 + NOFLSH = 57 + TOSTOP = 58 + IEXTEN = 59 + ECHOCTL = 60 + ECHOKE = 61 + PENDIN = 62 + OPOST = 70 + OLCUC = 71 + ONLCR = 72 + OCRNL = 73 + ONOCR = 74 + ONLRET = 75 + CS7 = 90 + CS8 = 91 + PARENB = 92 + PARODD = 93 + TTY_OP_ISPEED = 128 + TTY_OP_OSPEED = 129 +) + +// A Session represents a connection to a remote command or shell. +type Session struct { + // Stdin specifies the remote process's standard input. + // If Stdin is nil, the remote process reads from an empty + // bytes.Buffer. + Stdin io.Reader + + // Stdout and Stderr specify the remote process's standard + // output and error. + // + // If either is nil, Run connects the corresponding file + // descriptor to an instance of ioutil.Discard. There is a + // fixed amount of buffering that is shared for the two streams. + // If either blocks it may eventually cause the remote + // command to block. + Stdout io.Writer + Stderr io.Writer + + ch Channel // the channel backing this session + started bool // true once Start, Run or Shell is invoked. + copyFuncs []func() error + errors chan error // one send per copyFunc + + // true if pipe method is active + stdinpipe, stdoutpipe, stderrpipe bool + + // stdinPipeWriter is non-nil if StdinPipe has not been called + // and Stdin was specified by the user; it is the write end of + // a pipe connecting Session.Stdin to the stdin channel. + stdinPipeWriter io.WriteCloser + + exitStatus chan error +} + +// SendRequest sends an out-of-band channel request on the SSH channel +// underlying the session. +func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) { + return s.ch.SendRequest(name, wantReply, payload) +} + +func (s *Session) Close() error { + return s.ch.Close() +} + +// RFC 4254 Section 6.4. +type setenvRequest struct { + Name string + Value string +} + +// Setenv sets an environment variable that will be applied to any +// command executed by Shell or Run. +func (s *Session) Setenv(name, value string) error { + msg := setenvRequest{ + Name: name, + Value: value, + } + ok, err := s.ch.SendRequest("env", true, Marshal(&msg)) + if err == nil && !ok { + err = errors.New("ssh: setenv failed") + } + return err +} + +// RFC 4254 Section 6.2. +type ptyRequestMsg struct { + Term string + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 + Modelist string +} + +// RequestPty requests the association of a pty with the session on the remote host. +func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error { + var tm []byte + for k, v := range termmodes { + kv := struct { + Key byte + Val uint32 + }{k, v} + + tm = append(tm, Marshal(&kv)...) + } + tm = append(tm, tty_OP_END) + req := ptyRequestMsg{ + Term: term, + Columns: uint32(w), + Rows: uint32(h), + Width: uint32(w * 8), + Height: uint32(h * 8), + Modelist: string(tm), + } + ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req)) + if err == nil && !ok { + err = errors.New("ssh: pty-req failed") + } + return err +} + +// RFC 4254 Section 6.5. +type subsystemRequestMsg struct { + Subsystem string +} + +// RequestSubsystem requests the association of a subsystem with the session on the remote host. +// A subsystem is a predefined command that runs in the background when the ssh session is initiated +func (s *Session) RequestSubsystem(subsystem string) error { + msg := subsystemRequestMsg{ + Subsystem: subsystem, + } + ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg)) + if err == nil && !ok { + err = errors.New("ssh: subsystem request failed") + } + return err +} + +// RFC 4254 Section 6.9. +type signalMsg struct { + Signal string +} + +// Signal sends the given signal to the remote process. +// sig is one of the SIG* constants. +func (s *Session) Signal(sig Signal) error { + msg := signalMsg{ + Signal: string(sig), + } + + _, err := s.ch.SendRequest("signal", false, Marshal(&msg)) + return err +} + +// RFC 4254 Section 6.5. +type execMsg struct { + Command string +} + +// Start runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start or Shell. +func (s *Session) Start(cmd string) error { + if s.started { + return errors.New("ssh: session already started") + } + req := execMsg{ + Command: cmd, + } + + ok, err := s.ch.SendRequest("exec", true, Marshal(&req)) + if err == nil && !ok { + err = fmt.Errorf("ssh: command %v failed", cmd) + } + if err != nil { + return err + } + return s.start() +} + +// Run runs cmd on the remote host. Typically, the remote +// server passes cmd to the shell for interpretation. +// A Session only accepts one call to Run, Start, Shell, Output, +// or CombinedOutput. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the command fails to run or doesn't complete successfully, the +// error is of type *ExitError. Other error types may be +// returned for I/O problems. +func (s *Session) Run(cmd string) error { + err := s.Start(cmd) + if err != nil { + return err + } + return s.Wait() +} + +// Output runs cmd on the remote host and returns its standard output. +func (s *Session) Output(cmd string) ([]byte, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + var b bytes.Buffer + s.Stdout = &b + err := s.Run(cmd) + return b.Bytes(), err +} + +type singleWriter struct { + b bytes.Buffer + mu sync.Mutex +} + +func (w *singleWriter) Write(p []byte) (int, error) { + w.mu.Lock() + defer w.mu.Unlock() + return w.b.Write(p) +} + +// CombinedOutput runs cmd on the remote host and returns its combined +// standard output and standard error. +func (s *Session) CombinedOutput(cmd string) ([]byte, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + if s.Stderr != nil { + return nil, errors.New("ssh: Stderr already set") + } + var b singleWriter + s.Stdout = &b + s.Stderr = &b + err := s.Run(cmd) + return b.b.Bytes(), err +} + +// Shell starts a login shell on the remote host. A Session only +// accepts one call to Run, Start, Shell, Output, or CombinedOutput. +func (s *Session) Shell() error { + if s.started { + return errors.New("ssh: session already started") + } + + ok, err := s.ch.SendRequest("shell", true, nil) + if err == nil && !ok { + return fmt.Errorf("ssh: cound not start shell") + } + if err != nil { + return err + } + return s.start() +} + +func (s *Session) start() error { + s.started = true + + type F func(*Session) + for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} { + setupFd(s) + } + + s.errors = make(chan error, len(s.copyFuncs)) + for _, fn := range s.copyFuncs { + go func(fn func() error) { + s.errors <- fn() + }(fn) + } + return nil +} + +// Wait waits for the remote command to exit. +// +// The returned error is nil if the command runs, has no problems +// copying stdin, stdout, and stderr, and exits with a zero exit +// status. +// +// If the command fails to run or doesn't complete successfully, the +// error is of type *ExitError. Other error types may be +// returned for I/O problems. +func (s *Session) Wait() error { + if !s.started { + return errors.New("ssh: session not started") + } + waitErr := <-s.exitStatus + + if s.stdinPipeWriter != nil { + s.stdinPipeWriter.Close() + } + var copyError error + for _ = range s.copyFuncs { + if err := <-s.errors; err != nil && copyError == nil { + copyError = err + } + } + if waitErr != nil { + return waitErr + } + return copyError +} + +func (s *Session) wait(reqs <-chan *Request) error { + wm := Waitmsg{status: -1} + // Wait for msg channel to be closed before returning. + for msg := range reqs { + switch msg.Type { + case "exit-status": + d := msg.Payload + wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3]) + case "exit-signal": + var sigval struct { + Signal string + CoreDumped bool + Error string + Lang string + } + if err := Unmarshal(msg.Payload, &sigval); err != nil { + return err + } + + // Must sanitize strings? + wm.signal = sigval.Signal + wm.msg = sigval.Error + wm.lang = sigval.Lang + default: + // This handles keepalives and matches + // OpenSSH's behaviour. + if msg.WantReply { + msg.Reply(false, nil) + } + } + } + if wm.status == 0 { + return nil + } + if wm.status == -1 { + // exit-status was never sent from server + if wm.signal == "" { + return errors.New("wait: remote command exited without exit status or exit signal") + } + wm.status = 128 + if _, ok := signals[Signal(wm.signal)]; ok { + wm.status += signals[Signal(wm.signal)] + } + } + return &ExitError{wm} +} + +func (s *Session) stdin() { + if s.stdinpipe { + return + } + var stdin io.Reader + if s.Stdin == nil { + stdin = new(bytes.Buffer) + } else { + r, w := io.Pipe() + go func() { + _, err := io.Copy(w, s.Stdin) + w.CloseWithError(err) + }() + stdin, s.stdinPipeWriter = r, w + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.ch, stdin) + if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF { + err = err1 + } + return err + }) +} + +func (s *Session) stdout() { + if s.stdoutpipe { + return + } + if s.Stdout == nil { + s.Stdout = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.Stdout, s.ch) + return err + }) +} + +func (s *Session) stderr() { + if s.stderrpipe { + return + } + if s.Stderr == nil { + s.Stderr = ioutil.Discard + } + s.copyFuncs = append(s.copyFuncs, func() error { + _, err := io.Copy(s.Stderr, s.ch.Stderr()) + return err + }) +} + +// sessionStdin reroutes Close to CloseWrite. +type sessionStdin struct { + io.Writer + ch Channel +} + +func (s *sessionStdin) Close() error { + return s.ch.CloseWrite() +} + +// StdinPipe returns a pipe that will be connected to the +// remote command's standard input when the command starts. +func (s *Session) StdinPipe() (io.WriteCloser, error) { + if s.Stdin != nil { + return nil, errors.New("ssh: Stdin already set") + } + if s.started { + return nil, errors.New("ssh: StdinPipe after process started") + } + s.stdinpipe = true + return &sessionStdin{s.ch, s.ch}, nil +} + +// StdoutPipe returns a pipe that will be connected to the +// remote command's standard output when the command starts. +// There is a fixed amount of buffering that is shared between +// stdout and stderr streams. If the StdoutPipe reader is +// not serviced fast enough it may eventually cause the +// remote command to block. +func (s *Session) StdoutPipe() (io.Reader, error) { + if s.Stdout != nil { + return nil, errors.New("ssh: Stdout already set") + } + if s.started { + return nil, errors.New("ssh: StdoutPipe after process started") + } + s.stdoutpipe = true + return s.ch, nil +} + +// StderrPipe returns a pipe that will be connected to the +// remote command's standard error when the command starts. +// There is a fixed amount of buffering that is shared between +// stdout and stderr streams. If the StderrPipe reader is +// not serviced fast enough it may eventually cause the +// remote command to block. +func (s *Session) StderrPipe() (io.Reader, error) { + if s.Stderr != nil { + return nil, errors.New("ssh: Stderr already set") + } + if s.started { + return nil, errors.New("ssh: StderrPipe after process started") + } + s.stderrpipe = true + return s.ch.Stderr(), nil +} + +// newSession returns a new interactive session on the remote host. +func newSession(ch Channel, reqs <-chan *Request) (*Session, error) { + s := &Session{ + ch: ch, + } + s.exitStatus = make(chan error, 1) + go func() { + s.exitStatus <- s.wait(reqs) + }() + + return s, nil +} + +// An ExitError reports unsuccessful completion of a remote command. +type ExitError struct { + Waitmsg +} + +func (e *ExitError) Error() string { + return e.Waitmsg.String() +} + +// Waitmsg stores the information about an exited remote command +// as reported by Wait. +type Waitmsg struct { + status int + signal string + msg string + lang string +} + +// ExitStatus returns the exit status of the remote command. +func (w Waitmsg) ExitStatus() int { + return w.status +} + +// Signal returns the exit signal of the remote command if +// it was terminated violently. +func (w Waitmsg) Signal() string { + return w.signal +} + +// Msg returns the exit message given by the remote command +func (w Waitmsg) Msg() string { + return w.msg +} + +// Lang returns the language tag. See RFC 3066 +func (w Waitmsg) Lang() string { + return w.lang +} + +func (w Waitmsg) String() string { + return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal) +} diff --git a/vendor/golang.org/x/crypto/ssh/session_test.go b/vendor/golang.org/x/crypto/ssh/session_test.go new file mode 100644 index 0000000..78a7031 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/session_test.go @@ -0,0 +1,774 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +// Session tests. + +import ( + "bytes" + crypto_rand "crypto/rand" + "errors" + "io" + "io/ioutil" + "math/rand" + "net" + "testing" + + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh/terminal" +) + +type serverType func(Channel, <-chan *Request, *testing.T) + +// dial constructs a new test server and returns a *ClientConn. +func dial(handler serverType, t *testing.T) *Client { + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + + go func() { + defer c1.Close() + conf := ServerConfig{ + NoClientAuth: true, + } + conf.AddHostKey(testSigners["rsa"]) + + _, chans, reqs, err := NewServerConn(c1, &conf) + if err != nil { + t.Fatalf("Unable to handshake: %v", err) + } + go DiscardRequests(reqs) + + for newCh := range chans { + if newCh.ChannelType() != "session" { + newCh.Reject(UnknownChannelType, "unknown channel type") + continue + } + + ch, inReqs, err := newCh.Accept() + if err != nil { + t.Errorf("Accept: %v", err) + continue + } + go func() { + handler(ch, inReqs, t) + }() + } + }() + + config := &ClientConfig{ + User: "testuser", + } + + conn, chans, reqs, err := NewClientConn(c2, "", config) + if err != nil { + t.Fatalf("unable to dial remote side: %v", err) + } + + return NewClient(conn, chans, reqs) +} + +// Test a simple string is returned to session.Stdout. +func TestSessionShell(t *testing.T) { + conn := dial(shellHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + stdout := new(bytes.Buffer) + session.Stdout = stdout + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + if err := session.Wait(); err != nil { + t.Fatalf("Remote command did not exit cleanly: %v", err) + } + actual := stdout.String() + if actual != "golang" { + t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) + } +} + +// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. + +// Test a simple string is returned via StdoutPipe. +func TestSessionStdoutPipe(t *testing.T) { + conn := dial(shellHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + stdout, err := session.StdoutPipe() + if err != nil { + t.Fatalf("Unable to request StdoutPipe(): %v", err) + } + var buf bytes.Buffer + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + done := make(chan bool, 1) + go func() { + if _, err := io.Copy(&buf, stdout); err != nil { + t.Errorf("Copy of stdout failed: %v", err) + } + done <- true + }() + if err := session.Wait(); err != nil { + t.Fatalf("Remote command did not exit cleanly: %v", err) + } + <-done + actual := buf.String() + if actual != "golang" { + t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) + } +} + +// Test that a simple string is returned via the Output helper, +// and that stderr is discarded. +func TestSessionOutput(t *testing.T) { + conn := dial(fixedOutputHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + + buf, err := session.Output("") // cmd is ignored by fixedOutputHandler + if err != nil { + t.Error("Remote command did not exit cleanly:", err) + } + w := "this-is-stdout." + g := string(buf) + if g != w { + t.Error("Remote command did not return expected string:") + t.Logf("want %q", w) + t.Logf("got %q", g) + } +} + +// Test that both stdout and stderr are returned +// via the CombinedOutput helper. +func TestSessionCombinedOutput(t *testing.T) { + conn := dial(fixedOutputHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + + buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler + if err != nil { + t.Error("Remote command did not exit cleanly:", err) + } + const stdout = "this-is-stdout." + const stderr = "this-is-stderr." + g := string(buf) + if g != stdout+stderr && g != stderr+stdout { + t.Error("Remote command did not return expected string:") + t.Logf("want %q, or %q", stdout+stderr, stderr+stdout) + t.Logf("got %q", g) + } +} + +// Test non-0 exit status is returned correctly. +func TestExitStatusNonZero(t *testing.T) { + conn := dial(exitStatusNonZeroHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.ExitStatus() != 15 { + t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus()) + } +} + +// Test 0 exit status is returned correctly. +func TestExitStatusZero(t *testing.T) { + conn := dial(exitStatusZeroHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err != nil { + t.Fatalf("expected nil but got %v", err) + } +} + +// Test exit signal and status are both returned correctly. +func TestExitSignalAndStatus(t *testing.T) { + conn := dial(exitSignalAndStatusHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.Signal() != "TERM" || e.ExitStatus() != 15 { + t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) + } +} + +// Test exit signal and status are both returned correctly. +func TestKnownExitSignalOnly(t *testing.T) { + conn := dial(exitSignalHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.Signal() != "TERM" || e.ExitStatus() != 143 { + t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) + } +} + +// Test exit signal and status are both returned correctly. +func TestUnknownExitSignal(t *testing.T) { + conn := dial(exitSignalUnknownHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.Signal() != "SYS" || e.ExitStatus() != 128 { + t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) + } +} + +// Test WaitMsg is not returned if the channel closes abruptly. +func TestExitWithoutStatusOrSignal(t *testing.T) { + conn := dial(exitWithoutSignalOrStatus, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %v", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + _, ok := err.(*ExitError) + if ok { + // you can't actually test for errors.errorString + // because it's not exported. + t.Fatalf("expected *errorString but got %T", err) + } +} + +// windowTestBytes is the number of bytes that we'll send to the SSH server. +const windowTestBytes = 16000 * 200 + +// TestServerWindow writes random data to the server. The server is expected to echo +// the same data back, which is compared against the original. +func TestServerWindow(t *testing.T) { + origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) + io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes) + origBytes := origBuf.Bytes() + + conn := dial(echoHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatal(err) + } + defer session.Close() + result := make(chan []byte) + + go func() { + defer close(result) + echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) + serverStdout, err := session.StdoutPipe() + if err != nil { + t.Errorf("StdoutPipe failed: %v", err) + return + } + n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes) + if err != nil && err != io.EOF { + t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err) + } + result <- echoedBuf.Bytes() + }() + + serverStdin, err := session.StdinPipe() + if err != nil { + t.Fatalf("StdinPipe failed: %v", err) + } + written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes) + if err != nil { + t.Fatalf("failed to copy origBuf to serverStdin: %v", err) + } + if written != windowTestBytes { + t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes) + } + + echoedBytes := <-result + + if !bytes.Equal(origBytes, echoedBytes) { + t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes)) + } +} + +// Verify the client can handle a keepalive packet from the server. +func TestClientHandlesKeepalives(t *testing.T) { + conn := dial(channelKeepaliveSender, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatal(err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %v", err) + } + err = session.Wait() + if err != nil { + t.Fatalf("expected nil but got: %v", err) + } +} + +type exitStatusMsg struct { + Status uint32 +} + +type exitSignalMsg struct { + Signal string + CoreDumped bool + Errmsg string + Lang string +} + +func handleTerminalRequests(in <-chan *Request) { + for req := range in { + ok := false + switch req.Type { + case "shell": + ok = true + if len(req.Payload) > 0 { + // We don't accept any commands, only the default shell. + ok = false + } + case "env": + ok = true + } + req.Reply(ok, nil) + } +} + +func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal { + term := terminal.NewTerminal(ch, prompt) + go handleTerminalRequests(in) + return term +} + +func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + // this string is returned to stdout + shell := newServerShell(ch, in, "> ") + readLine(shell, t) + sendStatus(0, ch, t) +} + +func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + shell := newServerShell(ch, in, "> ") + readLine(shell, t) + sendStatus(15, ch, t) +} + +func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + shell := newServerShell(ch, in, "> ") + readLine(shell, t) + sendStatus(15, ch, t) + sendSignal("TERM", ch, t) +} + +func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + shell := newServerShell(ch, in, "> ") + readLine(shell, t) + sendSignal("TERM", ch, t) +} + +func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + shell := newServerShell(ch, in, "> ") + readLine(shell, t) + sendSignal("SYS", ch, t) +} + +func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + shell := newServerShell(ch, in, "> ") + readLine(shell, t) +} + +func shellHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + // this string is returned to stdout + shell := newServerShell(ch, in, "golang") + readLine(shell, t) + sendStatus(0, ch, t) +} + +// Ignores the command, writes fixed strings to stderr and stdout. +// Strings are "this-is-stdout." and "this-is-stderr.". +func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + _, err := ch.Read(nil) + + req, ok := <-in + if !ok { + t.Fatalf("error: expected channel request, got: %#v", err) + return + } + + // ignore request, always send some text + req.Reply(true, nil) + + _, err = io.WriteString(ch, "this-is-stdout.") + if err != nil { + t.Fatalf("error writing on server: %v", err) + } + _, err = io.WriteString(ch.Stderr(), "this-is-stderr.") + if err != nil { + t.Fatalf("error writing on server: %v", err) + } + sendStatus(0, ch, t) +} + +func readLine(shell *terminal.Terminal, t *testing.T) { + if _, err := shell.ReadLine(); err != nil && err != io.EOF { + t.Errorf("unable to read line: %v", err) + } +} + +func sendStatus(status uint32, ch Channel, t *testing.T) { + msg := exitStatusMsg{ + Status: status, + } + if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil { + t.Errorf("unable to send status: %v", err) + } +} + +func sendSignal(signal string, ch Channel, t *testing.T) { + sig := exitSignalMsg{ + Signal: signal, + CoreDumped: false, + Errmsg: "Process terminated", + Lang: "en-GB-oed", + } + if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil { + t.Errorf("unable to send signal: %v", err) + } +} + +func discardHandler(ch Channel, t *testing.T) { + defer ch.Close() + io.Copy(ioutil.Discard, ch) +} + +func echoHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil { + t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err) + } +} + +// copyNRandomly copies n bytes from src to dst. It uses a variable, and random, +// buffer size to exercise more code paths. +func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) { + var ( + buf = make([]byte, 32*1024) + written int + remaining = n + ) + for remaining > 0 { + l := rand.Intn(1 << 15) + if remaining < l { + l = remaining + } + nr, er := src.Read(buf[:l]) + nw, ew := dst.Write(buf[:nr]) + remaining -= nw + written += nw + if ew != nil { + return written, ew + } + if nr != nw { + return written, io.ErrShortWrite + } + if er != nil && er != io.EOF { + return written, er + } + } + return written, nil +} + +func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + shell := newServerShell(ch, in, "> ") + readLine(shell, t) + if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil { + t.Errorf("unable to send channel keepalive request: %v", err) + } + sendStatus(0, ch, t) +} + +func TestClientWriteEOF(t *testing.T) { + conn := dial(simpleEchoHandler, t) + defer conn.Close() + + session, err := conn.NewSession() + if err != nil { + t.Fatal(err) + } + defer session.Close() + stdin, err := session.StdinPipe() + if err != nil { + t.Fatalf("StdinPipe failed: %v", err) + } + stdout, err := session.StdoutPipe() + if err != nil { + t.Fatalf("StdoutPipe failed: %v", err) + } + + data := []byte(`0000`) + _, err = stdin.Write(data) + if err != nil { + t.Fatalf("Write failed: %v", err) + } + stdin.Close() + + res, err := ioutil.ReadAll(stdout) + if err != nil { + t.Fatalf("Read failed: %v", err) + } + + if !bytes.Equal(data, res) { + t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res) + } +} + +func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) { + defer ch.Close() + data, err := ioutil.ReadAll(ch) + if err != nil { + t.Errorf("handler read error: %v", err) + } + _, err = ch.Write(data) + if err != nil { + t.Errorf("handler write error: %v", err) + } +} + +func TestSessionID(t *testing.T) { + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + serverID := make(chan []byte, 1) + clientID := make(chan []byte, 1) + + serverConf := &ServerConfig{ + NoClientAuth: true, + } + serverConf.AddHostKey(testSigners["ecdsa"]) + clientConf := &ClientConfig{ + User: "user", + } + + go func() { + conn, chans, reqs, err := NewServerConn(c1, serverConf) + if err != nil { + t.Fatalf("server handshake: %v", err) + } + serverID <- conn.SessionID() + go DiscardRequests(reqs) + for ch := range chans { + ch.Reject(Prohibited, "") + } + }() + + go func() { + conn, chans, reqs, err := NewClientConn(c2, "", clientConf) + if err != nil { + t.Fatalf("client handshake: %v", err) + } + clientID <- conn.SessionID() + go DiscardRequests(reqs) + for ch := range chans { + ch.Reject(Prohibited, "") + } + }() + + s := <-serverID + c := <-clientID + if bytes.Compare(s, c) != 0 { + t.Errorf("server session ID (%x) != client session ID (%x)", s, c) + } else if len(s) == 0 { + t.Errorf("client and server SessionID were empty.") + } +} + +type noReadConn struct { + readSeen bool + net.Conn +} + +func (c *noReadConn) Close() error { + return nil +} + +func (c *noReadConn) Read(b []byte) (int, error) { + c.readSeen = true + return 0, errors.New("noReadConn error") +} + +func TestInvalidServerConfiguration(t *testing.T) { + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + serveConn := noReadConn{Conn: c1} + serverConf := &ServerConfig{} + + NewServerConn(&serveConn, serverConf) + if serveConn.readSeen { + t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing host key") + } + + serverConf.AddHostKey(testSigners["ecdsa"]) + + NewServerConn(&serveConn, serverConf) + if serveConn.readSeen { + t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method") + } +} + +func TestHostKeyAlgorithms(t *testing.T) { + serverConf := &ServerConfig{ + NoClientAuth: true, + } + serverConf.AddHostKey(testSigners["rsa"]) + serverConf.AddHostKey(testSigners["ecdsa"]) + + connect := func(clientConf *ClientConfig, want string) { + var alg string + clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error { + alg = key.Type() + return nil + } + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + go NewServerConn(c1, serverConf) + _, _, _, err = NewClientConn(c2, "", clientConf) + if err != nil { + t.Fatalf("NewClientConn: %v", err) + } + if alg != want { + t.Errorf("selected key algorithm %s, want %s", alg, want) + } + } + + // By default, we get the preferred algorithm, which is ECDSA 256. + + clientConf := &ClientConfig{} + connect(clientConf, KeyAlgoECDSA256) + + // Client asks for RSA explicitly. + clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA} + connect(clientConf, KeyAlgoRSA) + + c1, c2, err := netPipe() + if err != nil { + t.Fatalf("netPipe: %v", err) + } + defer c1.Close() + defer c2.Close() + + go NewServerConn(c1, serverConf) + clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"} + _, _, _, err = NewClientConn(c2, "", clientConf) + if err == nil { + t.Fatal("succeeded connecting with unknown hostkey algorithm") + } +} diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go new file mode 100644 index 0000000..6151241 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -0,0 +1,407 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "errors" + "fmt" + "io" + "math/rand" + "net" + "strconv" + "strings" + "sync" + "time" +) + +// Listen requests the remote peer open a listening socket on +// addr. Incoming connections will be available by calling Accept on +// the returned net.Listener. The listener must be serviced, or the +// SSH connection may hang. +func (c *Client) Listen(n, addr string) (net.Listener, error) { + laddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.ListenTCP(laddr) +} + +// Automatic port allocation is broken with OpenSSH before 6.0. See +// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017. In +// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0, +// rather than the actual port number. This means you can never open +// two different listeners with auto allocated ports. We work around +// this by trying explicit ports until we succeed. + +const openSSHPrefix = "OpenSSH_" + +var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano())) + +// isBrokenOpenSSHVersion returns true if the given version string +// specifies a version of OpenSSH that is known to have a bug in port +// forwarding. +func isBrokenOpenSSHVersion(versionStr string) bool { + i := strings.Index(versionStr, openSSHPrefix) + if i < 0 { + return false + } + i += len(openSSHPrefix) + j := i + for ; j < len(versionStr); j++ { + if versionStr[j] < '0' || versionStr[j] > '9' { + break + } + } + version, _ := strconv.Atoi(versionStr[i:j]) + return version < 6 +} + +// autoPortListenWorkaround simulates automatic port allocation by +// trying random ports repeatedly. +func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) { + var sshListener net.Listener + var err error + const tries = 10 + for i := 0; i < tries; i++ { + addr := *laddr + addr.Port = 1024 + portRandomizer.Intn(60000) + sshListener, err = c.ListenTCP(&addr) + if err == nil { + laddr.Port = addr.Port + return sshListener, err + } + } + return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err) +} + +// RFC 4254 7.1 +type channelForwardMsg struct { + addr string + rport uint32 +} + +// ListenTCP requests the remote peer open a listening socket +// on laddr. Incoming connections will be available by calling +// Accept on the returned net.Listener. +func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) { + if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) { + return c.autoPortListenWorkaround(laddr) + } + + m := channelForwardMsg{ + laddr.IP.String(), + uint32(laddr.Port), + } + // send message + ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m)) + if err != nil { + return nil, err + } + if !ok { + return nil, errors.New("ssh: tcpip-forward request denied by peer") + } + + // If the original port was 0, then the remote side will + // supply a real port number in the response. + if laddr.Port == 0 { + var p struct { + Port uint32 + } + if err := Unmarshal(resp, &p); err != nil { + return nil, err + } + laddr.Port = int(p.Port) + } + + // Register this forward, using the port number we obtained. + ch := c.forwards.add(*laddr) + + return &tcpListener{laddr, c, ch}, nil +} + +// forwardList stores a mapping between remote +// forward requests and the tcpListeners. +type forwardList struct { + sync.Mutex + entries []forwardEntry +} + +// forwardEntry represents an established mapping of a laddr on a +// remote ssh server to a channel connected to a tcpListener. +type forwardEntry struct { + laddr net.TCPAddr + c chan forward +} + +// forward represents an incoming forwarded tcpip connection. The +// arguments to add/remove/lookup should be address as specified in +// the original forward-request. +type forward struct { + newCh NewChannel // the ssh client channel underlying this forward + raddr *net.TCPAddr // the raddr of the incoming connection +} + +func (l *forwardList) add(addr net.TCPAddr) chan forward { + l.Lock() + defer l.Unlock() + f := forwardEntry{ + addr, + make(chan forward, 1), + } + l.entries = append(l.entries, f) + return f.c +} + +// See RFC 4254, section 7.2 +type forwardedTCPPayload struct { + Addr string + Port uint32 + OriginAddr string + OriginPort uint32 +} + +// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr. +func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) { + if port == 0 || port > 65535 { + return nil, fmt.Errorf("ssh: port number out of range: %d", port) + } + ip := net.ParseIP(string(addr)) + if ip == nil { + return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr) + } + return &net.TCPAddr{IP: ip, Port: int(port)}, nil +} + +func (l *forwardList) handleChannels(in <-chan NewChannel) { + for ch := range in { + var payload forwardedTCPPayload + if err := Unmarshal(ch.ExtraData(), &payload); err != nil { + ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error()) + continue + } + + // RFC 4254 section 7.2 specifies that incoming + // addresses should list the address, in string + // format. It is implied that this should be an IP + // address, as it would be impossible to connect to it + // otherwise. + laddr, err := parseTCPAddr(payload.Addr, payload.Port) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort) + if err != nil { + ch.Reject(ConnectionFailed, err.Error()) + continue + } + + if ok := l.forward(*laddr, *raddr, ch); !ok { + // Section 7.2, implementations MUST reject spurious incoming + // connections. + ch.Reject(Prohibited, "no forward for address") + continue + } + } +} + +// remove removes the forward entry, and the channel feeding its +// listener. +func (l *forwardList) remove(addr net.TCPAddr) { + l.Lock() + defer l.Unlock() + for i, f := range l.entries { + if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { + l.entries = append(l.entries[:i], l.entries[i+1:]...) + close(f.c) + return + } + } +} + +// closeAll closes and clears all forwards. +func (l *forwardList) closeAll() { + l.Lock() + defer l.Unlock() + for _, f := range l.entries { + close(f.c) + } + l.entries = nil +} + +func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool { + l.Lock() + defer l.Unlock() + for _, f := range l.entries { + if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port { + f.c <- forward{ch, &raddr} + return true + } + } + return false +} + +type tcpListener struct { + laddr *net.TCPAddr + + conn *Client + in <-chan forward +} + +// Accept waits for and returns the next connection to the listener. +func (l *tcpListener) Accept() (net.Conn, error) { + s, ok := <-l.in + if !ok { + return nil, io.EOF + } + ch, incoming, err := s.newCh.Accept() + if err != nil { + return nil, err + } + go DiscardRequests(incoming) + + return &tcpChanConn{ + Channel: ch, + laddr: l.laddr, + raddr: s.raddr, + }, nil +} + +// Close closes the listener. +func (l *tcpListener) Close() error { + m := channelForwardMsg{ + l.laddr.IP.String(), + uint32(l.laddr.Port), + } + + // this also closes the listener. + l.conn.forwards.remove(*l.laddr) + ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m)) + if err == nil && !ok { + err = errors.New("ssh: cancel-tcpip-forward failed") + } + return err +} + +// Addr returns the listener's network address. +func (l *tcpListener) Addr() net.Addr { + return l.laddr +} + +// Dial initiates a connection to the addr from the remote host. +// The resulting connection has a zero LocalAddr() and RemoteAddr(). +func (c *Client) Dial(n, addr string) (net.Conn, error) { + // Parse the address into host and numeric port. + host, portString, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + port, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return nil, err + } + // Use a zero address for local and remote address. + zeroAddr := &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port)) + if err != nil { + return nil, err + } + return &tcpChanConn{ + Channel: ch, + laddr: zeroAddr, + raddr: zeroAddr, + }, nil +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { + if laddr == nil { + laddr = &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + } + ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) + if err != nil { + return nil, err + } + return &tcpChanConn{ + Channel: ch, + laddr: laddr, + raddr: raddr, + }, nil +} + +// RFC 4254 7.2 +type channelOpenDirectMsg struct { + raddr string + rport uint32 + laddr string + lport uint32 +} + +func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) { + msg := channelOpenDirectMsg{ + raddr: raddr, + rport: uint32(rport), + laddr: laddr, + lport: uint32(lport), + } + ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg)) + if err != nil { + return nil, err + } + go DiscardRequests(in) + return ch, err +} + +type tcpChan struct { + Channel // the backing channel +} + +// tcpChanConn fulfills the net.Conn interface without +// the tcpChan having to hold laddr or raddr directly. +type tcpChanConn struct { + Channel + laddr, raddr net.Addr +} + +// LocalAddr returns the local network address. +func (t *tcpChanConn) LocalAddr() net.Addr { + return t.laddr +} + +// RemoteAddr returns the remote network address. +func (t *tcpChanConn) RemoteAddr() net.Addr { + return t.raddr +} + +// SetDeadline sets the read and write deadlines associated +// with the connection. +func (t *tcpChanConn) SetDeadline(deadline time.Time) error { + if err := t.SetReadDeadline(deadline); err != nil { + return err + } + return t.SetWriteDeadline(deadline) +} + +// SetReadDeadline sets the read deadline. +// A zero value for t means Read will not time out. +// After the deadline, the error from Read will implement net.Error +// with Timeout() == true. +func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error { + return errors.New("ssh: tcpChan: deadline not supported") +} + +// SetWriteDeadline exists to satisfy the net.Conn interface +// but is not implemented by this type. It always returns an error. +func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error { + return errors.New("ssh: tcpChan: deadline not supported") +} diff --git a/vendor/golang.org/x/crypto/ssh/tcpip_test.go b/vendor/golang.org/x/crypto/ssh/tcpip_test.go new file mode 100644 index 0000000..f1265cb --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/tcpip_test.go @@ -0,0 +1,20 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "testing" +) + +func TestAutoPortListenBroken(t *testing.T) { + broken := "SSH-2.0-OpenSSH_5.9hh11" + works := "SSH-2.0-OpenSSH_6.1" + if !isBrokenOpenSSHVersion(broken) { + t.Errorf("version %q not marked as broken", broken) + } + if isBrokenOpenSSHVersion(works) { + t.Errorf("version %q marked as broken", works) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go new file mode 100644 index 0000000..741eeb1 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal.go @@ -0,0 +1,892 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +import ( + "bytes" + "io" + "sync" + "unicode/utf8" +) + +// EscapeCodes contains escape sequences that can be written to the terminal in +// order to achieve different styles of text. +type EscapeCodes struct { + // Foreground colors + Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte + + // Reset all attributes + Reset []byte +} + +var vt100EscapeCodes = EscapeCodes{ + Black: []byte{keyEscape, '[', '3', '0', 'm'}, + Red: []byte{keyEscape, '[', '3', '1', 'm'}, + Green: []byte{keyEscape, '[', '3', '2', 'm'}, + Yellow: []byte{keyEscape, '[', '3', '3', 'm'}, + Blue: []byte{keyEscape, '[', '3', '4', 'm'}, + Magenta: []byte{keyEscape, '[', '3', '5', 'm'}, + Cyan: []byte{keyEscape, '[', '3', '6', 'm'}, + White: []byte{keyEscape, '[', '3', '7', 'm'}, + + Reset: []byte{keyEscape, '[', '0', 'm'}, +} + +// Terminal contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Terminal struct { + // AutoCompleteCallback, if non-null, is called for each keypress with + // the full input line and the current position of the cursor (in + // bytes, as an index into |line|). If it returns ok=false, the key + // press is processed normally. Otherwise it returns a replacement line + // and the new cursor position. + AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) + + // Escape contains a pointer to the escape codes for this terminal. + // It's always a valid pointer, although the escape codes themselves + // may be empty if the terminal doesn't support them. + Escape *EscapeCodes + + // lock protects the terminal and the state in this object from + // concurrent processing of a key press and a Write() call. + lock sync.Mutex + + c io.ReadWriter + prompt []rune + + // line is the current line being entered. + line []rune + // pos is the logical position of the cursor in line + pos int + // echo is true if local echo is enabled + echo bool + // pasteActive is true iff there is a bracketed paste operation in + // progress. + pasteActive bool + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int + + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte + + // history contains previously entered commands so that they can be + // accessed with the up and down keys. + history stRingBuffer + // historyIndex stores the currently accessed history entry, where zero + // means the immediately previous entry. + historyIndex int + // When navigating up and down the history it's possible to return to + // the incomplete, initial line. That value is stored in + // historyPending. + historyPending string +} + +// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewTerminal(c io.ReadWriter, prompt string) *Terminal { + return &Terminal{ + Escape: &vt100EscapeCodes, + c: c, + prompt: []rune(prompt), + termWidth: 80, + termHeight: 24, + echo: true, + historyIndex: -1, + } +} + +const ( + keyCtrlD = 4 + keyCtrlU = 21 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight + keyHome + keyEnd + keyDeleteWord + keyDeleteLine + keyClearScreen + keyPasteStart + keyPasteEnd +) + +var pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'} +var pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'} + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns utf8.RuneError. +func bytesToKey(b []byte, pasteActive bool) (rune, []byte) { + if len(b) == 0 { + return utf8.RuneError, nil + } + + if !pasteActive { + switch b[0] { + case 1: // ^A + return keyHome, b[1:] + case 5: // ^E + return keyEnd, b[1:] + case 8: // ^H + return keyBackspace, b[1:] + case 11: // ^K + return keyDeleteLine, b[1:] + case 12: // ^L + return keyClearScreen, b[1:] + case 23: // ^W + return keyDeleteWord, b[1:] + } + } + + if b[0] != keyEscape { + if !utf8.FullRune(b) { + return utf8.RuneError, b + } + r, l := utf8.DecodeRune(b) + return r, b[l:] + } + + if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + case 'H': + return keyHome, b[3:] + case 'F': + return keyEnd, b[3:] + } + } + + if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) { + return keyPasteStart, b[6:] + } + + if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) { + return keyPasteEnd, b[6:] + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z~] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' { + return keyUnknown, b[i+1:] + } + } + + return utf8.RuneError, b +} + +// queue appends data to the end of t.outBuf +func (t *Terminal) queue(data []rune) { + t.outBuf = append(t.outBuf, []byte(string(data))...) +} + +var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'} +var space = []rune{' '} + +func isPrintable(key rune) bool { + isInSurrogateArea := key >= 0xd800 && key <= 0xdbff + return key >= 32 && !isInSurrogateArea +} + +// moveCursorToPos appends data to t.outBuf which will move the cursor to the +// given, logical position in the text. +func (t *Terminal) moveCursorToPos(pos int) { + if !t.echo { + return + } + + x := visualLength(t.prompt) + pos + y := x / t.termWidth + x = x % t.termWidth + + up := 0 + if y < t.cursorY { + up = t.cursorY - y + } + + down := 0 + if y > t.cursorY { + down = y - t.cursorY + } + + left := 0 + if x < t.cursorX { + left = t.cursorX - x + } + + right := 0 + if x > t.cursorX { + right = x - t.cursorX + } + + t.cursorX = x + t.cursorY = y + t.move(up, down, left, right) +} + +func (t *Terminal) move(up, down, left, right int) { + movement := make([]rune, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + t.queue(movement) +} + +func (t *Terminal) clearLineToRight() { + op := []rune{keyEscape, '[', 'K'} + t.queue(op) +} + +const maxLineLength = 4096 + +func (t *Terminal) setLine(newLine []rune, newPos int) { + if t.echo { + t.moveCursorToPos(0) + t.writeLine(newLine) + for i := len(newLine); i < len(t.line); i++ { + t.writeLine(space) + } + t.moveCursorToPos(newPos) + } + t.line = newLine + t.pos = newPos +} + +func (t *Terminal) advanceCursor(places int) { + t.cursorX += places + t.cursorY += t.cursorX / t.termWidth + if t.cursorY > t.maxLine { + t.maxLine = t.cursorY + } + t.cursorX = t.cursorX % t.termWidth + + if places > 0 && t.cursorX == 0 { + // Normally terminals will advance the current position + // when writing a character. But that doesn't happen + // for the last character in a line. However, when + // writing a character (except a new line) that causes + // a line wrap, the position will be advanced two + // places. + // + // So, if we are stopping at the end of a line, we + // need to write a newline so that our cursor can be + // advanced to the next line. + t.outBuf = append(t.outBuf, '\n') + } +} + +func (t *Terminal) eraseNPreviousChars(n int) { + if n == 0 { + return + } + + if t.pos < n { + n = t.pos + } + t.pos -= n + t.moveCursorToPos(t.pos) + + copy(t.line[t.pos:], t.line[n+t.pos:]) + t.line = t.line[:len(t.line)-n] + if t.echo { + t.writeLine(t.line[t.pos:]) + for i := 0; i < n; i++ { + t.queue(space) + } + t.advanceCursor(n) + t.moveCursorToPos(t.pos) + } +} + +// countToLeftWord returns then number of characters from the cursor to the +// start of the previous word. +func (t *Terminal) countToLeftWord() int { + if t.pos == 0 { + return 0 + } + + pos := t.pos - 1 + for pos > 0 { + if t.line[pos] != ' ' { + break + } + pos-- + } + for pos > 0 { + if t.line[pos] == ' ' { + pos++ + break + } + pos-- + } + + return t.pos - pos +} + +// countToRightWord returns then number of characters from the cursor to the +// start of the next word. +func (t *Terminal) countToRightWord() int { + pos := t.pos + for pos < len(t.line) { + if t.line[pos] == ' ' { + break + } + pos++ + } + for pos < len(t.line) { + if t.line[pos] != ' ' { + break + } + pos++ + } + return pos - t.pos +} + +// visualLength returns the number of visible glyphs in s. +func visualLength(runes []rune) int { + inEscapeSeq := false + length := 0 + + for _, r := range runes { + switch { + case inEscapeSeq: + if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') { + inEscapeSeq = false + } + case r == '\x1b': + inEscapeSeq = true + default: + length++ + } + } + + return length +} + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (t *Terminal) handleKey(key rune) (line string, ok bool) { + if t.pasteActive && key != keyEnter { + t.addKeyToLine(key) + return + } + + switch key { + case keyBackspace: + if t.pos == 0 { + return + } + t.eraseNPreviousChars(1) + case keyAltLeft: + // move left by a word. + t.pos -= t.countToLeftWord() + t.moveCursorToPos(t.pos) + case keyAltRight: + // move right by a word. + t.pos += t.countToRightWord() + t.moveCursorToPos(t.pos) + case keyLeft: + if t.pos == 0 { + return + } + t.pos-- + t.moveCursorToPos(t.pos) + case keyRight: + if t.pos == len(t.line) { + return + } + t.pos++ + t.moveCursorToPos(t.pos) + case keyHome: + if t.pos == 0 { + return + } + t.pos = 0 + t.moveCursorToPos(t.pos) + case keyEnd: + if t.pos == len(t.line) { + return + } + t.pos = len(t.line) + t.moveCursorToPos(t.pos) + case keyUp: + entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) + if !ok { + return "", false + } + if t.historyIndex == -1 { + t.historyPending = string(t.line) + } + t.historyIndex++ + runes := []rune(entry) + t.setLine(runes, len(runes)) + case keyDown: + switch t.historyIndex { + case -1: + return + case 0: + runes := []rune(t.historyPending) + t.setLine(runes, len(runes)) + t.historyIndex-- + default: + entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) + if ok { + t.historyIndex-- + runes := []rune(entry) + t.setLine(runes, len(runes)) + } + } + case keyEnter: + t.moveCursorToPos(len(t.line)) + t.queue([]rune("\r\n")) + line = string(t.line) + ok = true + t.line = t.line[:0] + t.pos = 0 + t.cursorX = 0 + t.cursorY = 0 + t.maxLine = 0 + case keyDeleteWord: + // Delete zero or more spaces and then one or more characters. + t.eraseNPreviousChars(t.countToLeftWord()) + case keyDeleteLine: + // Delete everything from the current cursor position to the + // end of line. + for i := t.pos; i < len(t.line); i++ { + t.queue(space) + t.advanceCursor(1) + } + t.line = t.line[:t.pos] + t.moveCursorToPos(t.pos) + case keyCtrlD: + // Erase the character under the current position. + // The EOF case when the line is empty is handled in + // readLine(). + if t.pos < len(t.line) { + t.pos++ + t.eraseNPreviousChars(1) + } + case keyCtrlU: + t.eraseNPreviousChars(t.pos) + case keyClearScreen: + // Erases the screen and moves the cursor to the home position. + t.queue([]rune("\x1b[2J\x1b[H")) + t.queue(t.prompt) + t.cursorX, t.cursorY = 0, 0 + t.advanceCursor(visualLength(t.prompt)) + t.setLine(t.line, t.pos) + default: + if t.AutoCompleteCallback != nil { + prefix := string(t.line[:t.pos]) + suffix := string(t.line[t.pos:]) + + t.lock.Unlock() + newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key) + t.lock.Lock() + + if completeOk { + t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos])) + return + } + } + if !isPrintable(key) { + return + } + if len(t.line) == maxLineLength { + return + } + t.addKeyToLine(key) + } + return +} + +// addKeyToLine inserts the given key at the current position in the current +// line. +func (t *Terminal) addKeyToLine(key rune) { + if len(t.line) == cap(t.line) { + newLine := make([]rune, len(t.line), 2*(1+len(t.line))) + copy(newLine, t.line) + t.line = newLine + } + t.line = t.line[:len(t.line)+1] + copy(t.line[t.pos+1:], t.line[t.pos:]) + t.line[t.pos] = key + if t.echo { + t.writeLine(t.line[t.pos:]) + } + t.pos++ + t.moveCursorToPos(t.pos) +} + +func (t *Terminal) writeLine(line []rune) { + for len(line) != 0 { + remainingOnLine := t.termWidth - t.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine + } + t.queue(line[:todo]) + t.advanceCursor(visualLength(line[:todo])) + line = line[todo:] + } +} + +func (t *Terminal) Write(buf []byte) (n int, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + if t.cursorX == 0 && t.cursorY == 0 { + // This is the easy case: there's nothing on the screen that we + // have to move out of the way. + return t.c.Write(buf) + } + + // We have a prompt and possibly user input on the screen. We + // have to clear it first. + t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */) + t.cursorX = 0 + t.clearLineToRight() + + for t.cursorY > 0 { + t.move(1 /* up */, 0, 0, 0) + t.cursorY-- + t.clearLineToRight() + } + + if _, err = t.c.Write(t.outBuf); err != nil { + return + } + t.outBuf = t.outBuf[:0] + + if n, err = t.c.Write(buf); err != nil { + return + } + + t.writeLine(t.prompt) + if t.echo { + t.writeLine(t.line) + } + + t.moveCursorToPos(t.pos) + + if _, err = t.c.Write(t.outBuf); err != nil { + return + } + t.outBuf = t.outBuf[:0] + return +} + +// ReadPassword temporarily changes the prompt and reads a password, without +// echo, from the terminal. +func (t *Terminal) ReadPassword(prompt string) (line string, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + oldPrompt := t.prompt + t.prompt = []rune(prompt) + t.echo = false + + line, err = t.readLine() + + t.prompt = oldPrompt + t.echo = true + + return +} + +// ReadLine returns a line of input from the terminal. +func (t *Terminal) ReadLine() (line string, err error) { + t.lock.Lock() + defer t.lock.Unlock() + + return t.readLine() +} + +func (t *Terminal) readLine() (line string, err error) { + // t.lock must be held at this point + + if t.cursorX == 0 && t.cursorY == 0 { + t.writeLine(t.prompt) + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + } + + lineIsPasted := t.pasteActive + + for { + rest := t.remainder + lineOk := false + for !lineOk { + var key rune + key, rest = bytesToKey(rest, t.pasteActive) + if key == utf8.RuneError { + break + } + if !t.pasteActive { + if key == keyCtrlD { + if len(t.line) == 0 { + return "", io.EOF + } + } + if key == keyPasteStart { + t.pasteActive = true + if len(t.line) == 0 { + lineIsPasted = true + } + continue + } + } else if key == keyPasteEnd { + t.pasteActive = false + continue + } + if !t.pasteActive { + lineIsPasted = false + } + line, lineOk = t.handleKey(key) + } + if len(rest) > 0 { + n := copy(t.inBuf[:], rest) + t.remainder = t.inBuf[:n] + } else { + t.remainder = nil + } + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + if lineOk { + if t.echo { + t.historyIndex = -1 + t.history.Add(line) + } + if lineIsPasted { + err = ErrPasteIndicator + } + return + } + + // t.remainder is a slice at the beginning of t.inBuf + // containing a partial key sequence + readBuf := t.inBuf[len(t.remainder):] + var n int + + t.lock.Unlock() + n, err = t.c.Read(readBuf) + t.lock.Lock() + + if err != nil { + return + } + + t.remainder = t.inBuf[:n+len(t.remainder)] + } + + panic("unreachable") // for Go 1.0. +} + +// SetPrompt sets the prompt to be used when reading subsequent lines. +func (t *Terminal) SetPrompt(prompt string) { + t.lock.Lock() + defer t.lock.Unlock() + + t.prompt = []rune(prompt) +} + +func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { + // Move cursor to column zero at the start of the line. + t.move(t.cursorY, 0, t.cursorX, 0) + t.cursorX, t.cursorY = 0, 0 + t.clearLineToRight() + for t.cursorY < numPrevLines { + // Move down a line + t.move(0, 1, 0, 0) + t.cursorY++ + t.clearLineToRight() + } + // Move back to beginning. + t.move(t.cursorY, 0, 0, 0) + t.cursorX, t.cursorY = 0, 0 + + t.queue(t.prompt) + t.advanceCursor(visualLength(t.prompt)) + t.writeLine(t.line) + t.moveCursorToPos(t.pos) +} + +func (t *Terminal) SetSize(width, height int) error { + t.lock.Lock() + defer t.lock.Unlock() + + if width == 0 { + width = 1 + } + + oldWidth := t.termWidth + t.termWidth, t.termHeight = width, height + + switch { + case width == oldWidth: + // If the width didn't change then nothing else needs to be + // done. + return nil + case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0: + // If there is nothing on current line and no prompt printed, + // just do nothing + return nil + case width < oldWidth: + // Some terminals (e.g. xterm) will truncate lines that were + // too long when shinking. Others, (e.g. gnome-terminal) will + // attempt to wrap them. For the former, repainting t.maxLine + // works great, but that behaviour goes badly wrong in the case + // of the latter because they have doubled every full line. + + // We assume that we are working on a terminal that wraps lines + // and adjust the cursor position based on every previous line + // wrapping and turning into two. This causes the prompt on + // xterms to move upwards, which isn't great, but it avoids a + // huge mess with gnome-terminal. + if t.cursorX >= t.termWidth { + t.cursorX = t.termWidth - 1 + } + t.cursorY *= 2 + t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2) + case width > oldWidth: + // If the terminal expands then our position calculations will + // be wrong in the future because we think the cursor is + // |t.pos| chars into the string, but there will be a gap at + // the end of any wrapped line. + // + // But the position will actually be correct until we move, so + // we can move back to the beginning and repaint everything. + t.clearAndRepaintLinePlusNPrevious(t.maxLine) + } + + _, err := t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + return err +} + +type pasteIndicatorError struct{} + +func (pasteIndicatorError) Error() string { + return "terminal: ErrPasteIndicator not correctly handled" +} + +// ErrPasteIndicator may be returned from ReadLine as the error, in addition +// to valid line data. It indicates that bracketed paste mode is enabled and +// that the returned line consists only of pasted data. Programs may wish to +// interpret pasted data more literally than typed data. +var ErrPasteIndicator = pasteIndicatorError{} + +// SetBracketedPasteMode requests that the terminal bracket paste operations +// with markers. Not all terminals support this but, if it is supported, then +// enabling this mode will stop any autocomplete callback from running due to +// pastes. Additionally, any lines that are completely pasted will be returned +// from ReadLine with the error set to ErrPasteIndicator. +func (t *Terminal) SetBracketedPasteMode(on bool) { + if on { + io.WriteString(t.c, "\x1b[?2004h") + } else { + io.WriteString(t.c, "\x1b[?2004l") + } +} + +// stRingBuffer is a ring buffer of strings. +type stRingBuffer struct { + // entries contains max elements. + entries []string + max int + // head contains the index of the element most recently added to the ring. + head int + // size contains the number of elements in the ring. + size int +} + +func (s *stRingBuffer) Add(a string) { + if s.entries == nil { + const defaultNumEntries = 100 + s.entries = make([]string, defaultNumEntries) + s.max = defaultNumEntries + } + + s.head = (s.head + 1) % s.max + s.entries[s.head] = a + if s.size < s.max { + s.size++ + } +} + +// NthPreviousEntry returns the value passed to the nth previous call to Add. +// If n is zero then the immediately prior value is returned, if one, then the +// next most recent, and so on. If such an element doesn't exist then ok is +// false. +func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { + if n >= s.size { + return "", false + } + index := s.head - n + if index < 0 { + index += s.max + } + return s.entries[index], true +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go b/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go new file mode 100644 index 0000000..a663fe4 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/terminal_test.go @@ -0,0 +1,269 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +import ( + "io" + "testing" +) + +type MockTerminal struct { + toSend []byte + bytesPerRead int + received []byte +} + +func (c *MockTerminal) Read(data []byte) (n int, err error) { + n = len(data) + if n == 0 { + return + } + if n > len(c.toSend) { + n = len(c.toSend) + } + if n == 0 { + return 0, io.EOF + } + if c.bytesPerRead > 0 && n > c.bytesPerRead { + n = c.bytesPerRead + } + copy(data, c.toSend[:n]) + c.toSend = c.toSend[n:] + return +} + +func (c *MockTerminal) Write(data []byte) (n int, err error) { + c.received = append(c.received, data...) + return len(data), nil +} + +func TestClose(t *testing.T) { + c := &MockTerminal{} + ss := NewTerminal(c, "> ") + line, err := ss.ReadLine() + if line != "" { + t.Errorf("Expected empty line but got: %s", line) + } + if err != io.EOF { + t.Errorf("Error should have been EOF but got: %s", err) + } +} + +var keyPressTests = []struct { + in string + line string + err error + throwAwayLines int +}{ + { + err: io.EOF, + }, + { + in: "\r", + line: "", + }, + { + in: "foo\r", + line: "foo", + }, + { + in: "a\x1b[Cb\r", // right + line: "ab", + }, + { + in: "a\x1b[Db\r", // left + line: "ba", + }, + { + in: "a\177b\r", // backspace + line: "b", + }, + { + in: "\x1b[A\r", // up + }, + { + in: "\x1b[B\r", // down + }, + { + in: "line\x1b[A\x1b[B\r", // up then down + line: "line", + }, + { + in: "line1\rline2\x1b[A\r", // recall previous line. + line: "line1", + throwAwayLines: 1, + }, + { + // recall two previous lines and append. + in: "line1\rline2\rline3\x1b[A\x1b[Axxx\r", + line: "line1xxx", + throwAwayLines: 2, + }, + { + // Ctrl-A to move to beginning of line followed by ^K to kill + // line. + in: "a b \001\013\r", + line: "", + }, + { + // Ctrl-A to move to beginning of line, Ctrl-E to move to end, + // finally ^K to kill nothing. + in: "a b \001\005\013\r", + line: "a b ", + }, + { + in: "\027\r", + line: "", + }, + { + in: "a\027\r", + line: "", + }, + { + in: "a \027\r", + line: "", + }, + { + in: "a b\027\r", + line: "a ", + }, + { + in: "a b \027\r", + line: "a ", + }, + { + in: "one two thr\x1b[D\027\r", + line: "one two r", + }, + { + in: "\013\r", + line: "", + }, + { + in: "a\013\r", + line: "a", + }, + { + in: "ab\x1b[D\013\r", + line: "a", + }, + { + in: "Ξεσκεπάζω\r", + line: "Ξεσκεπάζω", + }, + { + in: "£\r\x1b[A\177\r", // non-ASCII char, enter, up, backspace. + line: "", + throwAwayLines: 1, + }, + { + in: "£\r££\x1b[A\x1b[B\177\r", // non-ASCII char, enter, 2x non-ASCII, up, down, backspace, enter. + line: "£", + throwAwayLines: 1, + }, + { + // Ctrl-D at the end of the line should be ignored. + in: "a\004\r", + line: "a", + }, + { + // a, b, left, Ctrl-D should erase the b. + in: "ab\x1b[D\004\r", + line: "a", + }, + { + // a, b, c, d, left, left, ^U should erase to the beginning of + // the line. + in: "abcd\x1b[D\x1b[D\025\r", + line: "cd", + }, + { + // Bracketed paste mode: control sequences should be returned + // verbatim in paste mode. + in: "abc\x1b[200~de\177f\x1b[201~\177\r", + line: "abcde\177", + }, + { + // Enter in bracketed paste mode should still work. + in: "abc\x1b[200~d\refg\x1b[201~h\r", + line: "efgh", + throwAwayLines: 1, + }, + { + // Lines consisting entirely of pasted data should be indicated as such. + in: "\x1b[200~a\r", + line: "a", + err: ErrPasteIndicator, + }, +} + +func TestKeyPresses(t *testing.T) { + for i, test := range keyPressTests { + for j := 1; j < len(test.in); j++ { + c := &MockTerminal{ + toSend: []byte(test.in), + bytesPerRead: j, + } + ss := NewTerminal(c, "> ") + for k := 0; k < test.throwAwayLines; k++ { + _, err := ss.ReadLine() + if err != nil { + t.Errorf("Throwaway line %d from test %d resulted in error: %s", k, i, err) + } + } + line, err := ss.ReadLine() + if line != test.line { + t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) + break + } + if err != test.err { + t.Errorf("Error resulting from test %d (%d bytes per read) was '%v', expected '%v'", i, j, err, test.err) + break + } + } + } +} + +func TestPasswordNotSaved(t *testing.T) { + c := &MockTerminal{ + toSend: []byte("password\r\x1b[A\r"), + bytesPerRead: 1, + } + ss := NewTerminal(c, "> ") + pw, _ := ss.ReadPassword("> ") + if pw != "password" { + t.Fatalf("failed to read password, got %s", pw) + } + line, _ := ss.ReadLine() + if len(line) > 0 { + t.Fatalf("password was saved in history") + } +} + +var setSizeTests = []struct { + width, height int +}{ + {40, 13}, + {80, 24}, + {132, 43}, +} + +func TestTerminalSetSize(t *testing.T) { + for _, setSize := range setSizeTests { + c := &MockTerminal{ + toSend: []byte("password\r\x1b[A\r"), + bytesPerRead: 1, + } + ss := NewTerminal(c, "> ") + ss.SetSize(setSize.width, setSize.height) + pw, _ := ss.ReadPassword("Password: ") + if pw != "password" { + t.Fatalf("failed to read password, got %s", pw) + } + if string(c.received) != "Password: \r\n" { + t.Errorf("failed to set the temporary prompt expected %q, got %q", "Password: ", c.received) + } + } +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go new file mode 100644 index 0000000..b09ee8c --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go @@ -0,0 +1,128 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal // import "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh/terminal" + +import ( + "io" + "syscall" + "unsafe" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + return nil, err + } + + newState := oldState.termios + newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF + newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + var oldState State + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { + return nil, err + } + + return &oldState, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) + return err +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + var dimensions [4]uint16 + + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { + return -1, -1, err + } + return int(dimensions[1]), int(dimensions[0]), nil +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var oldState syscall.Termios + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0); err != 0 { + return nil, err + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + newState.Lflag |= syscall.ICANON | syscall.ISIG + newState.Iflag |= syscall.ICRNL + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { + return nil, err + } + + defer func() { + syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&oldState)), 0, 0, 0) + }() + + var buf [16]byte + var ret []byte + for { + n, err := syscall.Read(fd, buf[:]) + if err != nil { + return nil, err + } + if n == 0 { + if len(ret) == 0 { + return nil, io.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go new file mode 100644 index 0000000..9c1ffd1 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_bsd.go @@ -0,0 +1,12 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package terminal + +import "syscall" + +const ioctlReadTermios = syscall.TIOCGETA +const ioctlWriteTermios = syscall.TIOCSETA diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go new file mode 100644 index 0000000..5883b22 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_linux.go @@ -0,0 +1,11 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package terminal + +// These constants are declared here, rather than importing +// them from the syscall package as some syscall packages, even +// on linux, for example gccgo, do not declare them. +const ioctlReadTermios = 0x5401 // syscall.TCGETS +const ioctlWriteTermios = 0x5402 // syscall.TCSETS diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go new file mode 100644 index 0000000..2dd6c3d --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/terminal/util_windows.go @@ -0,0 +1,174 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +// Package terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "io" + "syscall" + "unsafe" +) + +const ( + enableLineInput = 2 + enableEchoInput = 4 + enableProcessedInput = 1 + enableWindowInput = 8 + enableMouseInput = 16 + enableInsertMode = 32 + enableQuickEditMode = 64 + enableExtendedFlags = 128 + enableAutoPosition = 256 + enableProcessedOutput = 1 + enableWrapAtEolOutput = 2 +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") +) + +type ( + short int16 + word uint16 + + coord struct { + x short + y short + } + smallRect struct { + left short + top short + right short + bottom short + } + consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord + } +) + +type State struct { + mode uint32 +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var st uint32 + r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + return r != 0 && e == 0 +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + st &^= (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) + _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) + if e != 0 { + return nil, error(e) + } + return &State{st}, nil +} + +// GetState returns the current state of a terminal which may be useful to +// restore the terminal after a signal. +func GetState(fd int) (*State, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + return &State{st}, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + _, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0) + return err +} + +// GetSize returns the dimensions of the given terminal. +func GetSize(fd int) (width, height int, err error) { + var info consoleScreenBufferInfo + _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0) + if e != 0 { + return 0, 0, error(e) + } + return int(info.size.x), int(info.size.y), nil +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var st uint32 + _, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) + if e != 0 { + return nil, error(e) + } + old := st + + st &^= (enableEchoInput) + st |= (enableProcessedInput | enableLineInput | enableProcessedOutput) + _, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0) + if e != 0 { + return nil, error(e) + } + + defer func() { + syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0) + }() + + var buf [16]byte + var ret []byte + for { + n, err := syscall.Read(syscall.Handle(fd), buf[:]) + if err != nil { + return nil, err + } + if n == 0 { + if len(ret) == 0 { + return nil, io.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + if n > 0 && buf[n-1] == '\r' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/testdata/doc.go b/vendor/golang.org/x/crypto/ssh/testdata/doc.go new file mode 100644 index 0000000..b0291aa --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/testdata/doc.go @@ -0,0 +1,8 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This package contains test data shared between the various subpackages of +// the golang.org/x/crypto/ssh package. Under no circumstance should +// this data be used for production code. +package testdata // import "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh/testdata" diff --git a/vendor/golang.org/x/crypto/ssh/testdata/keys.go b/vendor/golang.org/x/crypto/ssh/testdata/keys.go new file mode 100644 index 0000000..5ff1c0e --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/testdata/keys.go @@ -0,0 +1,43 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testdata + +var PEMBytes = map[string][]byte{ + "dsa": []byte(`-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQD6PDSEyXiI9jfNs97WuM46MSDCYlOqWw80ajN16AohtBncs1YB +lHk//dQOvCYOsYaE+gNix2jtoRjwXhDsc25/IqQbU1ahb7mB8/rsaILRGIbA5WH3 +EgFtJmXFovDz3if6F6TzvhFpHgJRmLYVR8cqsezL3hEZOvvs2iH7MorkxwIVAJHD +nD82+lxh2fb4PMsIiaXudAsBAoGAQRf7Q/iaPRn43ZquUhd6WwvirqUj+tkIu6eV +2nZWYmXLlqFQKEy4Tejl7Wkyzr2OSYvbXLzo7TNxLKoWor6ips0phYPPMyXld14r +juhT24CrhOzuLMhDduMDi032wDIZG4Y+K7ElU8Oufn8Sj5Wge8r6ANmmVgmFfynr +FhdYCngCgYEA3ucGJ93/Mx4q4eKRDxcWD3QzWyqpbRVRRV1Vmih9Ha/qC994nJFz +DQIdjxDIT2Rk2AGzMqFEB68Zc3O+Wcsmz5eWWzEwFxaTwOGWTyDqsDRLm3fD+QYj +nOwuxb0Kce+gWI8voWcqC9cyRm09jGzu2Ab3Bhtpg8JJ8L7gS3MRZK4CFEx4UAfY +Fmsr0W6fHB9nhS4/UXM8 +-----END DSA PRIVATE KEY----- +`), + "ecdsa": []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINGWx0zo6fhJ/0EAfrPzVFyFC9s18lBt3cRoEDhS3ARooAoGCCqGSM49 +AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+ +6/ZOXRnPmNAlLUqjShUsUBBngG0u2fqEqA== +-----END EC PRIVATE KEY----- +`), + "rsa": []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIBOwIBAAJBALdGZxkXDAjsYk10ihwU6Id2KeILz1TAJuoq4tOgDWxEEGeTrcld +r/ZwVaFzjWzxaf6zQIJbfaSEAhqD5yo72+sCAwEAAQJBAK8PEVU23Wj8mV0QjwcJ +tZ4GcTUYQL7cF4+ezTCE9a1NrGnCP2RuQkHEKxuTVrxXt+6OF15/1/fuXnxKjmJC +nxkCIQDaXvPPBi0c7vAxGwNY9726x01/dNbHCE0CBtcotobxpwIhANbbQbh3JHVW +2haQh4fAG5mhesZKAGcxTyv4mQ7uMSQdAiAj+4dzMpJWdSzQ+qGHlHMIBvVHLkqB +y2VdEyF7DPCZewIhAI7GOI/6LDIFOvtPo6Bj2nNmyQ1HU6k/LRtNIXi4c9NJAiAr +rrxx26itVhJmcvoUhOjwuzSlP2bE5VHAvkGB352YBg== +-----END RSA PRIVATE KEY----- +`), + "user": []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49 +AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD +PLL8IEwvYu2wq+lpXfGQnNMbzYf9gspG0w== +-----END EC PRIVATE KEY----- +`), +} diff --git a/vendor/golang.org/x/crypto/ssh/testdata_test.go b/vendor/golang.org/x/crypto/ssh/testdata_test.go new file mode 100644 index 0000000..191de72 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/testdata_test.go @@ -0,0 +1,63 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places: +// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three +// instances. + +package ssh + +import ( + "crypto/rand" + "fmt" + + "github.com/moul/ssh2docker/vendor/golang.org/x/crypto/ssh/testdata" +) + +var ( + testPrivateKeys map[string]interface{} + testSigners map[string]Signer + testPublicKeys map[string]PublicKey +) + +func init() { + var err error + + n := len(testdata.PEMBytes) + testPrivateKeys = make(map[string]interface{}, n) + testSigners = make(map[string]Signer, n) + testPublicKeys = make(map[string]PublicKey, n) + for t, k := range testdata.PEMBytes { + testPrivateKeys[t], err = ParseRawPrivateKey(k) + if err != nil { + panic(fmt.Sprintf("Unable to parse test key %s: %v", t, err)) + } + testSigners[t], err = NewSignerFromKey(testPrivateKeys[t]) + if err != nil { + panic(fmt.Sprintf("Unable to create signer for test key %s: %v", t, err)) + } + testPublicKeys[t] = testSigners[t].PublicKey() + } + + // Create a cert and sign it for use in tests. + testCert := &Certificate{ + Nonce: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil + ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage + ValidAfter: 0, // unix epoch + ValidBefore: CertTimeInfinity, // The end of currently representable time. + Reserved: []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil + Key: testPublicKeys["ecdsa"], + SignatureKey: testPublicKeys["rsa"], + Permissions: Permissions{ + CriticalOptions: map[string]string{}, + Extensions: map[string]string{}, + }, + } + testCert.SignCert(rand.Reader, testSigners["rsa"]) + testPrivateKeys["cert"] = testPrivateKeys["ecdsa"] + testSigners["cert"], err = NewCertSigner(testCert, testSigners["ecdsa"]) + if err != nil { + panic(fmt.Sprintf("Unable to create certificate signer: %v", err)) + } +} diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go new file mode 100644 index 0000000..8351d37 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -0,0 +1,332 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bufio" + "errors" + "io" +) + +const ( + gcmCipherID = "aes128-gcm@openssh.com" + aes128cbcID = "aes128-cbc" +) + +// packetConn represents a transport that implements packet based +// operations. +type packetConn interface { + // Encrypt and send a packet of data to the remote peer. + writePacket(packet []byte) error + + // Read a packet from the connection + readPacket() ([]byte, error) + + // Close closes the write-side of the connection. + Close() error +} + +// transport is the keyingTransport that implements the SSH packet +// protocol. +type transport struct { + reader connectionState + writer connectionState + + bufReader *bufio.Reader + bufWriter *bufio.Writer + rand io.Reader + + io.Closer + + // Initial H used for the session ID. Once assigned this does + // not change, even during subsequent key exchanges. + sessionID []byte +} + +// getSessionID returns the ID of the SSH connection. The return value +// should not be modified. +func (t *transport) getSessionID() []byte { + if t.sessionID == nil { + panic("session ID not set yet") + } + return t.sessionID +} + +// packetCipher represents a combination of SSH encryption/MAC +// protocol. A single instance should be used for one direction only. +type packetCipher interface { + // writePacket encrypts the packet and writes it to w. The + // contents of the packet are generally scrambled. + writePacket(seqnum uint32, w io.Writer, rand io.Reader, packet []byte) error + + // readPacket reads and decrypts a packet of data. The + // returned packet may be overwritten by future calls of + // readPacket. + readPacket(seqnum uint32, r io.Reader) ([]byte, error) +} + +// connectionState represents one side (read or write) of the +// connection. This is necessary because each direction has its own +// keys, and can even have its own algorithms +type connectionState struct { + packetCipher + seqNum uint32 + dir direction + pendingKeyChange chan packetCipher +} + +// prepareKeyChange sets up key material for a keychange. The key changes in +// both directions are triggered by reading and writing a msgNewKey packet +// respectively. +func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { + if t.sessionID == nil { + t.sessionID = kexResult.H + } + + kexResult.SessionID = t.sessionID + + if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { + return err + } else { + t.reader.pendingKeyChange <- ciph + } + + if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { + return err + } else { + t.writer.pendingKeyChange <- ciph + } + + return nil +} + +// Read and decrypt next packet. +func (t *transport) readPacket() ([]byte, error) { + return t.reader.readPacket(t.bufReader) +} + +func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { + packet, err := s.packetCipher.readPacket(s.seqNum, r) + s.seqNum++ + if err == nil && len(packet) == 0 { + err = errors.New("ssh: zero length packet") + } + + if len(packet) > 0 && packet[0] == msgNewKeys { + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher + default: + return nil, errors.New("ssh: got bogus newkeys message.") + } + } + + // The packet may point to an internal buffer, so copy the + // packet out here. + fresh := make([]byte, len(packet)) + copy(fresh, packet) + + return fresh, err +} + +func (t *transport) writePacket(packet []byte) error { + return t.writer.writePacket(t.bufWriter, t.rand, packet) +} + +func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { + changeKeys := len(packet) > 0 && packet[0] == msgNewKeys + + err := s.packetCipher.writePacket(s.seqNum, w, rand, packet) + if err != nil { + return err + } + if err = w.Flush(); err != nil { + return err + } + s.seqNum++ + if changeKeys { + select { + case cipher := <-s.pendingKeyChange: + s.packetCipher = cipher + default: + panic("ssh: no key material for msgNewKeys") + } + } + return err +} + +func newTransport(rwc io.ReadWriteCloser, rand io.Reader, isClient bool) *transport { + t := &transport{ + bufReader: bufio.NewReader(rwc), + bufWriter: bufio.NewWriter(rwc), + rand: rand, + reader: connectionState{ + packetCipher: &streamPacketCipher{cipher: noneCipher{}}, + pendingKeyChange: make(chan packetCipher, 1), + }, + writer: connectionState{ + packetCipher: &streamPacketCipher{cipher: noneCipher{}}, + pendingKeyChange: make(chan packetCipher, 1), + }, + Closer: rwc, + } + if isClient { + t.reader.dir = serverKeys + t.writer.dir = clientKeys + } else { + t.reader.dir = clientKeys + t.writer.dir = serverKeys + } + + return t +} + +type direction struct { + ivTag []byte + keyTag []byte + macKeyTag []byte +} + +var ( + serverKeys = direction{[]byte{'B'}, []byte{'D'}, []byte{'F'}} + clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} +) + +// generateKeys generates key material for IV, MAC and encryption. +func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) { + cipherMode := cipherModes[algs.Cipher] + macMode := macModes[algs.MAC] + + iv = make([]byte, cipherMode.ivSize) + key = make([]byte, cipherMode.keySize) + macKey = make([]byte, macMode.keySize) + + generateKeyMaterial(iv, d.ivTag, kex) + generateKeyMaterial(key, d.keyTag, kex) + generateKeyMaterial(macKey, d.macKeyTag, kex) + return +} + +// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as +// described in RFC 4253, section 6.4. direction should either be serverKeys +// (to setup server->client keys) or clientKeys (for client->server keys). +func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { + iv, key, macKey := generateKeys(d, algs, kex) + + if algs.Cipher == gcmCipherID { + return newGCMCipher(iv, key, macKey) + } + + if algs.Cipher == aes128cbcID { + return newAESCBCCipher(iv, key, macKey, algs) + } + + c := &streamPacketCipher{ + mac: macModes[algs.MAC].new(macKey), + } + c.macResult = make([]byte, c.mac.Size()) + + var err error + c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv) + if err != nil { + return nil, err + } + + return c, nil +} + +// generateKeyMaterial fills out with key material generated from tag, K, H +// and sessionId, as specified in RFC 4253, section 7.2. +func generateKeyMaterial(out, tag []byte, r *kexResult) { + var digestsSoFar []byte + + h := r.Hash.New() + for len(out) > 0 { + h.Reset() + h.Write(r.K) + h.Write(r.H) + + if len(digestsSoFar) == 0 { + h.Write(tag) + h.Write(r.SessionID) + } else { + h.Write(digestsSoFar) + } + + digest := h.Sum(nil) + n := copy(out, digest) + out = out[n:] + if len(out) > 0 { + digestsSoFar = append(digestsSoFar, digest...) + } + } +} + +const packageVersion = "SSH-2.0-Go" + +// Sends and receives a version line. The versionLine string should +// be US ASCII, start with "SSH-2.0-", and should not include a +// newline. exchangeVersions returns the other side's version line. +func exchangeVersions(rw io.ReadWriter, versionLine []byte) (them []byte, err error) { + // Contrary to the RFC, we do not ignore lines that don't + // start with "SSH-2.0-" to make the library usable with + // nonconforming servers. + for _, c := range versionLine { + // The spec disallows non US-ASCII chars, and + // specifically forbids null chars. + if c < 32 { + return nil, errors.New("ssh: junk character in version line") + } + } + if _, err = rw.Write(append(versionLine, '\r', '\n')); err != nil { + return + } + + them, err = readVersion(rw) + return them, err +} + +// maxVersionStringBytes is the maximum number of bytes that we'll +// accept as a version string. RFC 4253 section 4.2 limits this at 255 +// chars +const maxVersionStringBytes = 255 + +// Read version string as specified by RFC 4253, section 4.2. +func readVersion(r io.Reader) ([]byte, error) { + versionString := make([]byte, 0, 64) + var ok bool + var buf [1]byte + + for len(versionString) < maxVersionStringBytes { + _, err := io.ReadFull(r, buf[:]) + if err != nil { + return nil, err + } + // The RFC says that the version should be terminated with \r\n + // but several SSH servers actually only send a \n. + if buf[0] == '\n' { + ok = true + break + } + + // non ASCII chars are disallowed, but we are lenient, + // since Go doesn't use null-terminated strings. + + // The RFC allows a comment after a space, however, + // all of it (version and comments) goes into the + // session hash. + versionString = append(versionString, buf[0]) + } + + if !ok { + return nil, errors.New("ssh: overflow reading version string") + } + + // There might be a '\r' on the end which we should remove. + if len(versionString) > 0 && versionString[len(versionString)-1] == '\r' { + versionString = versionString[:len(versionString)-1] + } + return versionString, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/transport_test.go b/vendor/golang.org/x/crypto/ssh/transport_test.go new file mode 100644 index 0000000..92d83ab --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/transport_test.go @@ -0,0 +1,109 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "strings" + "testing" +) + +func TestReadVersion(t *testing.T) { + longversion := strings.Repeat("SSH-2.0-bla", 50)[:253] + cases := map[string]string{ + "SSH-2.0-bla\r\n": "SSH-2.0-bla", + "SSH-2.0-bla\n": "SSH-2.0-bla", + longversion + "\r\n": longversion, + } + + for in, want := range cases { + result, err := readVersion(bytes.NewBufferString(in)) + if err != nil { + t.Errorf("readVersion(%q): %s", in, err) + } + got := string(result) + if got != want { + t.Errorf("got %q, want %q", got, want) + } + } +} + +func TestReadVersionError(t *testing.T) { + longversion := strings.Repeat("SSH-2.0-bla", 50)[:253] + cases := []string{ + longversion + "too-long\r\n", + } + for _, in := range cases { + if _, err := readVersion(bytes.NewBufferString(in)); err == nil { + t.Errorf("readVersion(%q) should have failed", in) + } + } +} + +func TestExchangeVersionsBasic(t *testing.T) { + v := "SSH-2.0-bla" + buf := bytes.NewBufferString(v + "\r\n") + them, err := exchangeVersions(buf, []byte("xyz")) + if err != nil { + t.Errorf("exchangeVersions: %v", err) + } + + if want := "SSH-2.0-bla"; string(them) != want { + t.Errorf("got %q want %q for our version", them, want) + } +} + +func TestExchangeVersions(t *testing.T) { + cases := []string{ + "not\x000allowed", + "not allowed\n", + } + for _, c := range cases { + buf := bytes.NewBufferString("SSH-2.0-bla\r\n") + if _, err := exchangeVersions(buf, []byte(c)); err == nil { + t.Errorf("exchangeVersions(%q): should have failed", c) + } + } +} + +type closerBuffer struct { + bytes.Buffer +} + +func (b *closerBuffer) Close() error { + return nil +} + +func TestTransportMaxPacketWrite(t *testing.T) { + buf := &closerBuffer{} + tr := newTransport(buf, rand.Reader, true) + huge := make([]byte, maxPacket+1) + err := tr.writePacket(huge) + if err == nil { + t.Errorf("transport accepted write for a huge packet.") + } +} + +func TestTransportMaxPacketReader(t *testing.T) { + var header [5]byte + huge := make([]byte, maxPacket+128) + binary.BigEndian.PutUint32(header[0:], uint32(len(huge))) + // padding. + header[4] = 0 + + buf := &closerBuffer{} + buf.Write(header[:]) + buf.Write(huge) + + tr := newTransport(buf, rand.Reader, true) + _, err := tr.readPacket() + if err == nil { + t.Errorf("transport succeeded reading huge packet.") + } else if !strings.Contains(err.Error(), "large") { + t.Errorf("got %q, should mention %q", err.Error(), "large") + } +} diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go new file mode 100644 index 0000000..285ac53 --- /dev/null +++ b/vendor/golang.org/x/net/context/context.go @@ -0,0 +1,447 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context // import "github.com/moul/ssh2docker/vendor/golang.org/x/net/context" + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out <-chan Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it's is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, &c) + return &c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) cancelCtx { + return cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return &c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/vendor/golang.org/x/net/context/context_test.go b/vendor/golang.org/x/net/context/context_test.go new file mode 100644 index 0000000..e64afa6 --- /dev/null +++ b/vendor/golang.org/x/net/context/context_test.go @@ -0,0 +1,575 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context + +import ( + "fmt" + "math/rand" + "runtime" + "strings" + "sync" + "testing" + "time" +) + +// otherContext is a Context that's not one of the types defined in context.go. +// This lets us test code paths that differ based on the underlying type of the +// Context. +type otherContext struct { + Context +} + +func TestBackground(t *testing.T) { + c := Background() + if c == nil { + t.Fatalf("Background returned nil") + } + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + if got, want := fmt.Sprint(c), "context.Background"; got != want { + t.Errorf("Background().String() = %q want %q", got, want) + } +} + +func TestTODO(t *testing.T) { + c := TODO() + if c == nil { + t.Fatalf("TODO returned nil") + } + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + if got, want := fmt.Sprint(c), "context.TODO"; got != want { + t.Errorf("TODO().String() = %q want %q", got, want) + } +} + +func TestWithCancel(t *testing.T) { + c1, cancel := WithCancel(Background()) + + if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want { + t.Errorf("c1.String() = %q want %q", got, want) + } + + o := otherContext{c1} + c2, _ := WithCancel(o) + contexts := []Context{c1, o, c2} + + for i, c := range contexts { + if d := c.Done(); d == nil { + t.Errorf("c[%d].Done() == %v want non-nil", i, d) + } + if e := c.Err(); e != nil { + t.Errorf("c[%d].Err() == %v want nil", i, e) + } + + select { + case x := <-c.Done(): + t.Errorf("<-c.Done() == %v want nothing (it should block)", x) + default: + } + } + + cancel() + time.Sleep(100 * time.Millisecond) // let cancelation propagate + + for i, c := range contexts { + select { + case <-c.Done(): + default: + t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i) + } + if e := c.Err(); e != Canceled { + t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled) + } + } +} + +func TestParentFinishesChild(t *testing.T) { + // Context tree: + // parent -> cancelChild + // parent -> valueChild -> timerChild + parent, cancel := WithCancel(Background()) + cancelChild, stop := WithCancel(parent) + defer stop() + valueChild := WithValue(parent, "key", "value") + timerChild, stop := WithTimeout(valueChild, 10000*time.Hour) + defer stop() + + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + case x := <-cancelChild.Done(): + t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x) + case x := <-timerChild.Done(): + t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x) + case x := <-valueChild.Done(): + t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x) + default: + } + + // The parent's children should contain the two cancelable children. + pc := parent.(*cancelCtx) + cc := cancelChild.(*cancelCtx) + tc := timerChild.(*timerCtx) + pc.mu.Lock() + if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] { + t.Errorf("bad linkage: pc.children = %v, want %v and %v", + pc.children, cc, tc) + } + pc.mu.Unlock() + + if p, ok := parentCancelCtx(cc.Context); !ok || p != pc { + t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc) + } + if p, ok := parentCancelCtx(tc.Context); !ok || p != pc { + t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc) + } + + cancel() + + pc.mu.Lock() + if len(pc.children) != 0 { + t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children) + } + pc.mu.Unlock() + + // parent and children should all be finished. + check := func(ctx Context, name string) { + select { + case <-ctx.Done(): + default: + t.Errorf("<-%s.Done() blocked, but shouldn't have", name) + } + if e := ctx.Err(); e != Canceled { + t.Errorf("%s.Err() == %v want %v", name, e, Canceled) + } + } + check(parent, "parent") + check(cancelChild, "cancelChild") + check(valueChild, "valueChild") + check(timerChild, "timerChild") + + // WithCancel should return a canceled context on a canceled parent. + precanceledChild := WithValue(parent, "key", "value") + select { + case <-precanceledChild.Done(): + default: + t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have") + } + if e := precanceledChild.Err(); e != Canceled { + t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled) + } +} + +func TestChildFinishesFirst(t *testing.T) { + cancelable, stop := WithCancel(Background()) + defer stop() + for _, parent := range []Context{Background(), cancelable} { + child, cancel := WithCancel(parent) + + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + case x := <-child.Done(): + t.Errorf("<-child.Done() == %v want nothing (it should block)", x) + default: + } + + cc := child.(*cancelCtx) + pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background() + if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) { + t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok) + } + + if pcok { + pc.mu.Lock() + if len(pc.children) != 1 || !pc.children[cc] { + t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc) + } + pc.mu.Unlock() + } + + cancel() + + if pcok { + pc.mu.Lock() + if len(pc.children) != 0 { + t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children) + } + pc.mu.Unlock() + } + + // child should be finished. + select { + case <-child.Done(): + default: + t.Errorf("<-child.Done() blocked, but shouldn't have") + } + if e := child.Err(); e != Canceled { + t.Errorf("child.Err() == %v want %v", e, Canceled) + } + + // parent should not be finished. + select { + case x := <-parent.Done(): + t.Errorf("<-parent.Done() == %v want nothing (it should block)", x) + default: + } + if e := parent.Err(); e != nil { + t.Errorf("parent.Err() == %v want nil", e) + } + } +} + +func testDeadline(c Context, wait time.Duration, t *testing.T) { + select { + case <-time.After(wait): + t.Fatalf("context should have timed out") + case <-c.Done(): + } + if e := c.Err(); e != DeadlineExceeded { + t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded) + } +} + +func TestDeadline(t *testing.T) { + c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { + t.Errorf("c.String() = %q want prefix %q", got, prefix) + } + testDeadline(c, 200*time.Millisecond, t) + + c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + o := otherContext{c} + testDeadline(o, 200*time.Millisecond, t) + + c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond)) + o = otherContext{c} + c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond)) + testDeadline(c, 200*time.Millisecond, t) +} + +func TestTimeout(t *testing.T) { + c, _ := WithTimeout(Background(), 100*time.Millisecond) + if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) { + t.Errorf("c.String() = %q want prefix %q", got, prefix) + } + testDeadline(c, 200*time.Millisecond, t) + + c, _ = WithTimeout(Background(), 100*time.Millisecond) + o := otherContext{c} + testDeadline(o, 200*time.Millisecond, t) + + c, _ = WithTimeout(Background(), 100*time.Millisecond) + o = otherContext{c} + c, _ = WithTimeout(o, 300*time.Millisecond) + testDeadline(c, 200*time.Millisecond, t) +} + +func TestCanceledTimeout(t *testing.T) { + c, _ := WithTimeout(Background(), 200*time.Millisecond) + o := otherContext{c} + c, cancel := WithTimeout(o, 400*time.Millisecond) + cancel() + time.Sleep(100 * time.Millisecond) // let cancelation propagate + select { + case <-c.Done(): + default: + t.Errorf("<-c.Done() blocked, but shouldn't have") + } + if e := c.Err(); e != Canceled { + t.Errorf("c.Err() == %v want %v", e, Canceled) + } +} + +type key1 int +type key2 int + +var k1 = key1(1) +var k2 = key2(1) // same int as k1, different type +var k3 = key2(3) // same type as k2, different int + +func TestValues(t *testing.T) { + check := func(c Context, nm, v1, v2, v3 string) { + if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 { + t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0) + } + if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 { + t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0) + } + if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 { + t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0) + } + } + + c0 := Background() + check(c0, "c0", "", "", "") + + c1 := WithValue(Background(), k1, "c1k1") + check(c1, "c1", "c1k1", "", "") + + if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want { + t.Errorf("c.String() = %q want %q", got, want) + } + + c2 := WithValue(c1, k2, "c2k2") + check(c2, "c2", "c1k1", "c2k2", "") + + c3 := WithValue(c2, k3, "c3k3") + check(c3, "c2", "c1k1", "c2k2", "c3k3") + + c4 := WithValue(c3, k1, nil) + check(c4, "c4", "", "c2k2", "c3k3") + + o0 := otherContext{Background()} + check(o0, "o0", "", "", "") + + o1 := otherContext{WithValue(Background(), k1, "c1k1")} + check(o1, "o1", "c1k1", "", "") + + o2 := WithValue(o1, k2, "o2k2") + check(o2, "o2", "c1k1", "o2k2", "") + + o3 := otherContext{c4} + check(o3, "o3", "", "c2k2", "c3k3") + + o4 := WithValue(o3, k3, nil) + check(o4, "o4", "", "c2k2", "") +} + +func TestAllocs(t *testing.T) { + bg := Background() + for _, test := range []struct { + desc string + f func() + limit float64 + gccgoLimit float64 + }{ + { + desc: "Background()", + f: func() { Background() }, + limit: 0, + gccgoLimit: 0, + }, + { + desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1), + f: func() { + c := WithValue(bg, k1, nil) + c.Value(k1) + }, + limit: 3, + gccgoLimit: 3, + }, + { + desc: "WithTimeout(bg, 15*time.Millisecond)", + f: func() { + c, _ := WithTimeout(bg, 15*time.Millisecond) + <-c.Done() + }, + limit: 8, + gccgoLimit: 15, + }, + { + desc: "WithCancel(bg)", + f: func() { + c, cancel := WithCancel(bg) + cancel() + <-c.Done() + }, + limit: 5, + gccgoLimit: 8, + }, + { + desc: "WithTimeout(bg, 100*time.Millisecond)", + f: func() { + c, cancel := WithTimeout(bg, 100*time.Millisecond) + cancel() + <-c.Done() + }, + limit: 8, + gccgoLimit: 25, + }, + } { + limit := test.limit + if runtime.Compiler == "gccgo" { + // gccgo does not yet do escape analysis. + // TOOD(iant): Remove this when gccgo does do escape analysis. + limit = test.gccgoLimit + } + if n := testing.AllocsPerRun(100, test.f); n > limit { + t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit)) + } + } +} + +func TestSimultaneousCancels(t *testing.T) { + root, cancel := WithCancel(Background()) + m := map[Context]CancelFunc{root: cancel} + q := []Context{root} + // Create a tree of contexts. + for len(q) != 0 && len(m) < 100 { + parent := q[0] + q = q[1:] + for i := 0; i < 4; i++ { + ctx, cancel := WithCancel(parent) + m[ctx] = cancel + q = append(q, ctx) + } + } + // Start all the cancels in a random order. + var wg sync.WaitGroup + wg.Add(len(m)) + for _, cancel := range m { + go func(cancel CancelFunc) { + cancel() + wg.Done() + }(cancel) + } + // Wait on all the contexts in a random order. + for ctx := range m { + select { + case <-ctx.Done(): + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n]) + } + } + // Wait for all the cancel functions to return. + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + select { + case <-done: + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n]) + } +} + +func TestInterlockedCancels(t *testing.T) { + parent, cancelParent := WithCancel(Background()) + child, cancelChild := WithCancel(parent) + go func() { + parent.Done() + cancelChild() + }() + cancelParent() + select { + case <-child.Done(): + case <-time.After(1 * time.Second): + buf := make([]byte, 10<<10) + n := runtime.Stack(buf, true) + t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n]) + } +} + +func TestLayersCancel(t *testing.T) { + testLayers(t, time.Now().UnixNano(), false) +} + +func TestLayersTimeout(t *testing.T) { + testLayers(t, time.Now().UnixNano(), true) +} + +func testLayers(t *testing.T, seed int64, testTimeout bool) { + rand.Seed(seed) + errorf := func(format string, a ...interface{}) { + t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...) + } + const ( + timeout = 200 * time.Millisecond + minLayers = 30 + ) + type value int + var ( + vals []*value + cancels []CancelFunc + numTimers int + ctx = Background() + ) + for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ { + switch rand.Intn(3) { + case 0: + v := new(value) + ctx = WithValue(ctx, v, v) + vals = append(vals, v) + case 1: + var cancel CancelFunc + ctx, cancel = WithCancel(ctx) + cancels = append(cancels, cancel) + case 2: + var cancel CancelFunc + ctx, cancel = WithTimeout(ctx, timeout) + cancels = append(cancels, cancel) + numTimers++ + } + } + checkValues := func(when string) { + for _, key := range vals { + if val := ctx.Value(key).(*value); key != val { + errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key) + } + } + } + select { + case <-ctx.Done(): + errorf("ctx should not be canceled yet") + default: + } + if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) { + t.Errorf("ctx.String() = %q want prefix %q", s, prefix) + } + t.Log(ctx) + checkValues("before cancel") + if testTimeout { + select { + case <-ctx.Done(): + case <-time.After(timeout + timeout/10): + errorf("ctx should have timed out") + } + checkValues("after timeout") + } else { + cancel := cancels[rand.Intn(len(cancels))] + cancel() + select { + case <-ctx.Done(): + default: + errorf("ctx should be canceled") + } + checkValues("after cancel") + } +} + +func TestCancelRemoves(t *testing.T) { + checkChildren := func(when string, ctx Context, want int) { + if got := len(ctx.(*cancelCtx).children); got != want { + t.Errorf("%s: context has %d children, want %d", when, got, want) + } + } + + ctx, _ := WithCancel(Background()) + checkChildren("after creation", ctx, 0) + _, cancel := WithCancel(ctx) + checkChildren("with WithCancel child ", ctx, 1) + cancel() + checkChildren("after cancelling WithCancel child", ctx, 0) + + ctx, _ = WithCancel(Background()) + checkChildren("after creation", ctx, 0) + _, cancel = WithTimeout(ctx, 60*time.Minute) + checkChildren("with WithTimeout child ", ctx, 1) + cancel() + checkChildren("after cancelling WithTimeout child", ctx, 0) +} diff --git a/vendor/golang.org/x/net/context/withtimeout_test.go b/vendor/golang.org/x/net/context/withtimeout_test.go new file mode 100644 index 0000000..b56ca1d --- /dev/null +++ b/vendor/golang.org/x/net/context/withtimeout_test.go @@ -0,0 +1,26 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package context_test + +import ( + "fmt" + "time" + + "github.com/moul/ssh2docker/vendor/golang.org/x/net/context" +) + +func ExampleWithTimeout() { + // Pass a context with a timeout to tell a blocking function that it + // should abandon its work after the timeout elapses. + ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond) + select { + case <-time.After(200 * time.Millisecond): + fmt.Println("overslept") + case <-ctx.Done(): + fmt.Println(ctx.Err()) // prints "context deadline exceeded" + } + // Output: + // context deadline exceeded +}