From 27cc83140882b487f48daee467b37f4ca547c417 Mon Sep 17 00:00:00 2001 From: Random-Liu Date: Fri, 3 Feb 2017 03:06:44 -0800 Subject: [PATCH 1/3] Add arbitrary daemon log support --- config/docker-monitor-filelog.json | 14 ++ config/docker-monitor.json | 12 ++ config/kernel-monitor-filelog.json | 58 ++++++++ config/kernel-monitor.json | 3 + .../logwatchers/journald/log_watcher.go | 25 ++-- .../logwatchers/syslog/helpers.go | 128 +++++++++--------- .../logwatchers/syslog/helpers_test.go | 50 +++++-- .../logwatchers/syslog/log_watcher.go | 51 +++++-- .../logwatchers/syslog/log_watcher_test.go | 33 +++-- .../logwatchers/types/log_watcher.go | 3 + 10 files changed, 272 insertions(+), 105 deletions(-) create mode 100644 config/docker-monitor-filelog.json create mode 100644 config/docker-monitor.json create mode 100644 config/kernel-monitor-filelog.json diff --git a/config/docker-monitor-filelog.json b/config/docker-monitor-filelog.json new file mode 100644 index 00000000..d4ee0c62 --- /dev/null +++ b/config/docker-monitor-filelog.json @@ -0,0 +1,14 @@ +{ + "plugin": "syslog", + "pluginConfig": { + "timestamp": "^time=\"(\\S*)\"", + "message": "msg=\"([^\n]*)\"", + "timestampFormat": "2006-01-02T15:04:05.999999999-07:00" + }, + "logPath": "/var/log/docker.log", + "lookback": "5m", + "bufferSize": 10, + "source": "docker-monitor", + "conditions": [], + "rules": [] +} diff --git a/config/docker-monitor.json b/config/docker-monitor.json new file mode 100644 index 00000000..226e555c --- /dev/null +++ b/config/docker-monitor.json @@ -0,0 +1,12 @@ +{ + "plugin": "journald", + "pluginConfig": { + "source": "docker" + }, + "logPath": "/var/log/journal", + "lookback": "5m", + "bufferSize": 10, + "source": "docker-monitor", + "conditions": [], + "rules": [] +} diff --git a/config/kernel-monitor-filelog.json b/config/kernel-monitor-filelog.json new file mode 100644 index 00000000..e95b5ad1 --- /dev/null +++ b/config/kernel-monitor-filelog.json @@ -0,0 +1,58 @@ +{ + "plugin": "syslog", + "pluginConfig": { + "timestamp": "^.{15}", + "message": "kernel: \\[.*\\] (.*)", + "timestampFormat": "Jan _2 15:04:05" + }, + "logPath": "/var/log/kern.log", + "lookback": "5m", + "bufferSize": 10, + "source": "kernel-monitor", + "conditions": [ + { + "type": "KernelDeadlock", + "reason": "KernelHasNoDeadlock", + "message": "kernel has no deadlock" + } + ], + "rules": [ + { + "type": "temporary", + "reason": "OOMKilling", + "pattern": "Kill process \\d+ (.+) score \\d+ or sacrifice child\\nKilled process \\d+ (.+) total-vm:\\d+kB, anon-rss:\\d+kB, file-rss:\\d+kB" + }, + { + "type": "temporary", + "reason": "TaskHung", + "pattern": "task \\S+:\\w+ blocked for more than \\w+ seconds\\." + }, + { + "type": "temporary", + "reason": "UnregisterNetDevice", + "pattern": "unregister_netdevice: waiting for \\w+ to become free. Usage count = \\d+" + }, + { + "type": "temporary", + "reason": "KernelOops", + "pattern": "BUG: unable to handle kernel NULL pointer dereference at .*" + }, + { + "type": "temporary", + "reason": "KernelOops", + "pattern": "divide error: 0000 \\[#\\d+\\] SMP" + }, + { + "type": "permanent", + "condition": "KernelDeadlock", + "reason": "AUFSUmountHung", + "pattern": "task umount\\.aufs:\\w+ blocked for more than \\w+ seconds\\." + }, + { + "type": "permanent", + "condition": "KernelDeadlock", + "reason": "DockerHung", + "pattern": "task docker:\\w+ blocked for more than \\w+ seconds\\." + } + ] +} diff --git a/config/kernel-monitor.json b/config/kernel-monitor.json index 4b6e656d..dcd5113e 100644 --- a/config/kernel-monitor.json +++ b/config/kernel-monitor.json @@ -1,5 +1,8 @@ { "plugin": "journald", + "pluginConfig": { + "source": "kernel" + }, "logPath": "/var/log/journal", "lookback": "5m", "bufferSize": 10, diff --git a/pkg/kernelmonitor/logwatchers/journald/log_watcher.go b/pkg/kernelmonitor/logwatchers/journald/log_watcher.go index 7aecd5ac..aae33a71 100644 --- a/pkg/kernelmonitor/logwatchers/journald/log_watcher.go +++ b/pkg/kernelmonitor/logwatchers/journald/log_watcher.go @@ -114,8 +114,13 @@ func (j *journaldWatcher) watchLoop() { } } -// defaultJournalLogPath is the default path of journal log. -const defaultJournalLogPath = "/var/log/journal" +const ( + // defaultJournalLogPath is the default path of journal log. + defaultJournalLogPath = "/var/log/journal" + + // configSourceKey is the key of source configuration in the plugin configuration. + configSourceKey = "source" +) // getJournal returns a journal client. func getJournal(cfg types.WatcherConfig) (*sdjournal.Journal, error) { @@ -140,14 +145,18 @@ func getJournal(cfg types.WatcherConfig) (*sdjournal.Journal, error) { if err != nil { return nil, fmt.Errorf("failed to lookback %q: %v", since, err) } - // TODO(random-liu): Make this configurable to support parsing other logs. - kernelMatch := sdjournal.Match{ - Field: sdjournal.SD_JOURNAL_FIELD_TRANSPORT, - Value: "kernel", + // Empty source is not allowed and treated as an error. + source := cfg.PluginConfig[configSourceKey] + if source == "" { + return nil, fmt.Errorf("failed to filter journal log, empty source is not allowed") } - err = journal.AddMatch(kernelMatch.String()) + match := sdjournal.Match{ + Field: sdjournal.SD_JOURNAL_FIELD_SYSLOG_IDENTIFIER, + Value: source, + } + err = journal.AddMatch(match.String()) if err != nil { - return nil, fmt.Errorf("failed to add log filter %#v: %v", kernelMatch, err) + return nil, fmt.Errorf("failed to add log filter %#v: %v", match, err) } return journal, nil } diff --git a/pkg/kernelmonitor/logwatchers/syslog/helpers.go b/pkg/kernelmonitor/logwatchers/syslog/helpers.go index 2412c9bb..3f2ed9c3 100644 --- a/pkg/kernelmonitor/logwatchers/syslog/helpers.go +++ b/pkg/kernelmonitor/logwatchers/syslog/helpers.go @@ -17,86 +17,88 @@ package syslog import ( "fmt" - "io" - "os" - "strings" + "regexp" "time" kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types" - "github.com/google/cadvisor/utils/tail" + "github.com/golang/glog" ) -// translate translates the log line into internal type. -func translate(line string) (*kerntypes.KernelLog, error) { - timestamp, message, err := parseLine(line) - if err != nil { - return nil, err +// translator translates log line into internal log type based on user defined +// regular expression. +type translator struct { + timestampRegexp *regexp.Regexp + messageRegexp *regexp.Regexp + timestampFormat string +} + +const ( + // NOTE that we support submatch for both timestamp and message regular expressions. When + // there are multiple matches returned by submatch, only **the last** is used. + // timestampKey is the key of timestamp regular expression in the plugin configuration. + timestampKey = "timestamp" + // messageKey is the key of message regular expression in the plugin configuration. + messageKey = "message" + // timestampFormatKey is the key of timestamp format string in the plugin configuration. + timestampFormatKey = "timestampFormat" +) + +func newTranslatorOrDie(pluginConfig map[string]string) *translator { + if err := validatePluginConfig(pluginConfig); err != nil { + glog.Errorf("Failed to validate plugin configuration %+v: %v", pluginConfig, err) } + return &translator{ + timestampRegexp: regexp.MustCompile(pluginConfig[timestampKey]), + messageRegexp: regexp.MustCompile(pluginConfig[messageKey]), + timestampFormat: pluginConfig[timestampFormatKey], + } +} + +// translate translates the log line into internal type. +func (t *translator) translate(line string) (*kerntypes.KernelLog, error) { + // Parse timestamp. + matches := t.timestampRegexp.FindStringSubmatch(line) + if len(matches) == 0 { + return nil, fmt.Errorf("no timestamp found in line %q with regular expression %v", line, t.timestampRegexp) + } + timestamp, err := time.ParseInLocation(t.timestampFormat, matches[len(matches)-1], time.Local) + if err != nil { + return nil, fmt.Errorf("failed to parse timestamp %q: %v", matches[len(matches)-1], err) + } + // Formalize the timestmap. + timestamp = formalizeTimestamp(timestamp) + // Parse message. + matches = t.messageRegexp.FindStringSubmatch(line) + if len(matches) == 0 { + return nil, fmt.Errorf("no message found in line %q with regular expression %v", line, t.messageRegexp) + } + message := matches[len(matches)-1] return &kerntypes.KernelLog{ Timestamp: timestamp, Message: message, }, nil } -const ( - // timestampLen is the length of timestamp in syslog logging format. - timestampLen = 15 - // messagePrefix is the character before real message. - messagePrefix = "]" -) - -// parseLine parses one log line into timestamp and message. -func parseLine(line string) (time.Time, string, error) { - // Trim the spaces to make sure timestamp could be found - line = strings.TrimSpace(line) - if len(line) < timestampLen { - return time.Time{}, "", fmt.Errorf("the line is too short: %q", line) +// validatePluginConfig validates whether the plugin configuration. +func validatePluginConfig(cfg map[string]string) error { + if cfg[timestampKey] == "" { + return fmt.Errorf("unexpected empty timestamp regular expression") } - // Example line: Jan 1 00:00:00 hostname kernel: [0.000000] component: log message - now := time.Now() - // There is no time zone information in kernel log timestamp, apply the current time - // zone. - timestamp, err := time.ParseInLocation(time.Stamp, line[:timestampLen], time.Local) - if err != nil { - return time.Time{}, "", fmt.Errorf("error parsing timestamp in line %q: %v", line, err) + if cfg[messageKey] == "" { + return fmt.Errorf("unexpected empty message regular expression") } - // There is no year information in kernel log timestamp, apply the current year. - // This could go wrong during looking back phase after kernel monitor is started, - // and the old logs are generated in old year. - timestamp = timestamp.AddDate(now.Year(), 0, 0) - - loc := strings.Index(line, messagePrefix) - if loc == -1 { - return timestamp, "", fmt.Errorf("can't find message prefix %q in line %q", messagePrefix, line) + if cfg[timestampFormatKey] == "" { + return fmt.Errorf("unexpected empty timestamp format string") } - message := strings.Trim(line[loc+1:], " ") - - return timestamp, message, nil + return nil } -// defaultKernelLogPath the default path of syslog kernel log. -const defaultKernelLogPath = "/var/log/kern.log" - -// getLogReader returns log reader for syslog log. Note that getLogReader doesn't look back -// to the rolled out logs. -func getLogReader(path string) (io.ReadCloser, error) { - if path == "" { - path = defaultKernelLogPath +// formalizeTimestamp formalizes the timestamp. We need this because some log doesn't contain full +// timestamp, e.g. syslog. +func formalizeTimestamp(t time.Time) time.Time { + if t.Year() == 0 { + t = t.AddDate(time.Now().Year(), 0, 0) } - // To handle log rotation, tail will not report error immediately if - // the file doesn't exist. So we check file existence frist. - // This could go wrong during mid-rotation. It should recover after - // several restart when the log file is created again. The chance - // is slim but we should still fix this in the future. - // TODO(random-liu): Handle log missing during rotation. - _, err := os.Stat(path) - if err != nil { - return nil, fmt.Errorf("failed to stat the file %q: %v", path, err) - } - tail, err := tail.NewTail(path) - if err != nil { - return nil, fmt.Errorf("failed to tail the file %q: %v", path, err) - } - return tail, nil + return t } diff --git a/pkg/kernelmonitor/logwatchers/syslog/helpers_test.go b/pkg/kernelmonitor/logwatchers/syslog/helpers_test.go index c738ed9f..8e128c19 100644 --- a/pkg/kernelmonitor/logwatchers/syslog/helpers_test.go +++ b/pkg/kernelmonitor/logwatchers/syslog/helpers_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types" ) @@ -28,12 +29,18 @@ import ( func TestTranslate(t *testing.T) { year := time.Now().Year() testCases := []struct { - input string - err bool - log *kerntypes.KernelLog + config map[string]string + input string + err bool + log *kerntypes.KernelLog }{ { - input: "May 1 12:23:45 hostname kernel: [0.000000] component: log message", + // missing year and timezone + // "timestamp": "^.{15}", + // "message": "kernel \\[.*\\] (.*)", + // "timestampFormat": "Jan _2 15:04:05", + config: getTestPluginConfig(), + input: "May 1 12:23:45 hostname kernel: [0.000000] component: log message", log: &kerntypes.KernelLog{ Timestamp: time.Date(year, time.May, 1, 12, 23, 45, 0, time.Local), Message: "component: log message", @@ -41,7 +48,8 @@ func TestTranslate(t *testing.T) { }, { // no log message - input: "May 21 12:23:45 hostname kernel: [9.999999]", + config: getTestPluginConfig(), + input: "May 21 12:23:45 hostname kernel: [9.999999] ", log: &kerntypes.KernelLog{ Timestamp: time.Date(year, time.May, 21, 12, 23, 45, 0, time.Local), Message: "", @@ -49,18 +57,36 @@ func TestTranslate(t *testing.T) { }, { // the right square bracket is missing - input: "May 21 12:23:45 hostname kernel: [9.999999 component: log message", - err: true, + config: getTestPluginConfig(), + input: "May 21 12:23:45 hostname kernel: [9.999999 component: log message", + err: true, + }, + { + // contains full timestamp + config: map[string]string{ + "timestamp": "^time=\"(\\S*)\"", + "message": "msg=\"([^\n]*)\"", + "timestampFormat": "2006-01-02T15:04:05.999999999-07:00", + }, + input: `time="2017-02-01T17:58:34.999999999-08:00" level=error msg="test log line1\n test log line2"`, + log: &kerntypes.KernelLog{ + Timestamp: time.Date(2017, 2, 1, 17, 58, 34, 999999999, time.FixedZone("PST", -8*3600)), + Message: `test log line1\n test log line2`, + }, }, } for c, test := range testCases { t.Logf("TestCase #%d: %#v", c+1, test) - log, err := translate(test.input) - if (err != nil) != test.err { - t.Errorf("case %d: error assertion failed, got log: %+v, error: %v", c+1, log, err) - continue + trans := newTranslatorOrDie(test.config) + log, err := trans.translate(test.input) + if !test.err { + require.NoError(t, err) + // Use RFC3339Nano to make it easier for comparison. + assert.Equal(t, test.log.Timestamp.Format(time.RFC3339Nano), log.Timestamp.Format(time.RFC3339Nano)) + assert.Equal(t, test.log.Message, log.Message) + } else { + require.Error(t, err) } - assert.Equal(t, test.log, log) } } diff --git a/pkg/kernelmonitor/logwatchers/syslog/log_watcher.go b/pkg/kernelmonitor/logwatchers/syslog/log_watcher.go index 84545790..6b6df7c8 100644 --- a/pkg/kernelmonitor/logwatchers/syslog/log_watcher.go +++ b/pkg/kernelmonitor/logwatchers/syslog/log_watcher.go @@ -19,12 +19,16 @@ package syslog import ( "bufio" "bytes" + "fmt" "io" + "os" + "strings" "syscall" "time" utilclock "code.cloudfoundry.org/clock" "github.com/golang/glog" + "github.com/google/cadvisor/utils/tail" "k8s.io/node-problem-detector/pkg/kernelmonitor/logwatchers/types" kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types" @@ -32,13 +36,14 @@ import ( ) type syslogWatcher struct { - cfg types.WatcherConfig - reader *bufio.Reader - closer io.Closer - logCh chan *kerntypes.KernelLog - uptime time.Time - tomb *util.Tomb - clock utilclock.Clock + cfg types.WatcherConfig + reader *bufio.Reader + closer io.Closer + translator *translator + logCh chan *kerntypes.KernelLog + uptime time.Time + tomb *util.Tomb + clock utilclock.Clock } // NewSyslogWatcherOrDie creates a new kernel log watcher. The function panics @@ -49,9 +54,10 @@ func NewSyslogWatcherOrDie(cfg types.WatcherConfig) types.LogWatcher { glog.Fatalf("Failed to get system info: %v", err) } return &syslogWatcher{ - cfg: cfg, - uptime: time.Now().Add(time.Duration(-info.Uptime * int64(time.Second))), - tomb: util.NewTomb(), + cfg: cfg, + translator: newTranslatorOrDie(cfg.PluginConfig), + uptime: time.Now().Add(time.Duration(-info.Uptime * int64(time.Second))), + tomb: util.NewTomb(), // A capacity 1000 buffer should be enough logCh: make(chan *kerntypes.KernelLog, 1000), clock: utilclock.NewClock(), @@ -116,7 +122,7 @@ func (s *syslogWatcher) watchLoop() { } line = buffer.String() buffer.Reset() - log, err := translate(line) + log, err := s.translator.translate(strings.TrimSuffix(line, "\n")) if err != nil { glog.Warningf("Unable to parse line: %q, %v", line, err) continue @@ -128,3 +134,26 @@ func (s *syslogWatcher) watchLoop() { s.logCh <- log } } + +// getLogReader returns log reader for syslog log. Note that getLogReader doesn't look back +// to the rolled out logs. +func getLogReader(path string) (io.ReadCloser, error) { + if path == "" { + return nil, fmt.Errorf("unexpected empty log path") + } + // To handle log rotation, tail will not report error immediately if + // the file doesn't exist. So we check file existence first. + // This could go wrong during mid-rotation. It should recover after + // several restart when the log file is created again. The chance + // is slim but we should still fix this in the future. + // TODO(random-liu): Handle log missing during rotation. + _, err := os.Stat(path) + if err != nil { + return nil, fmt.Errorf("failed to stat the file %q: %v", path, err) + } + tail, err := tail.NewTail(path) + if err != nil { + return nil, fmt.Errorf("failed to tail the file %q: %v", path, err) + } + return tail, nil +} diff --git a/pkg/kernelmonitor/logwatchers/syslog/log_watcher_test.go b/pkg/kernelmonitor/logwatchers/syslog/log_watcher_test.go index 2886a021..5f1f376a 100644 --- a/pkg/kernelmonitor/logwatchers/syslog/log_watcher_test.go +++ b/pkg/kernelmonitor/logwatchers/syslog/log_watcher_test.go @@ -29,6 +29,16 @@ import ( "github.com/stretchr/testify/assert" ) +// getTestPluginConfig returns a plugin config for test. Use configuration for +// kernel log in test. +func getTestPluginConfig() map[string]string { + return map[string]string{ + "timestamp": "^.{15}", + "message": "kernel: \\[.*\\] (.*)", + "timestampFormat": "Jan _2 15:04:05", + } +} + func TestWatch(t *testing.T) { // now is a fake time now := time.Date(time.Now().Year(), time.January, 2, 3, 4, 5, 0, time.Local) @@ -42,8 +52,8 @@ func TestWatch(t *testing.T) { { // The start point is at the head of the log file. log: `Jan 2 03:04:05 kernel: [0.000000] 1 - Jan 2 03:04:06 kernel: [1.000000] 2 - Jan 2 03:04:07 kernel: [2.000000] 3 +Jan 2 03:04:06 kernel: [1.000000] 2 +Jan 2 03:04:07 kernel: [2.000000] 3 `, lookback: "0", logs: []kerntypes.KernelLog{ @@ -64,8 +74,8 @@ func TestWatch(t *testing.T) { { // The start point is in the middle of the log file. log: `Jan 2 03:04:04 kernel: [0.000000] 1 - Jan 2 03:04:05 kernel: [1.000000] 2 - Jan 2 03:04:06 kernel: [2.000000] 3 +Jan 2 03:04:05 kernel: [1.000000] 2 +Jan 2 03:04:06 kernel: [2.000000] 3 `, lookback: "0", logs: []kerntypes.KernelLog{ @@ -82,8 +92,8 @@ func TestWatch(t *testing.T) { { // The start point is at the end of the log file, but we look back. log: `Jan 2 03:04:03 kernel: [0.000000] 1 - Jan 2 03:04:04 kernel: [1.000000] 2 - Jan 2 03:04:05 kernel: [2.000000] 3 +Jan 2 03:04:04 kernel: [1.000000] 2 +Jan 2 03:04:05 kernel: [2.000000] 3 `, lookback: "1s", logs: []kerntypes.KernelLog{ @@ -101,8 +111,8 @@ func TestWatch(t *testing.T) { // The start point is at the end of the log file, we look back, but // system rebooted at in the middle of the log file. log: `Jan 2 03:04:03 kernel: [0.000000] 1 - Jan 2 03:04:04 kernel: [1.000000] 2 - Jan 2 03:04:05 kernel: [2.000000] 3 +Jan 2 03:04:04 kernel: [1.000000] 2 +Jan 2 03:04:05 kernel: [2.000000] 3 `, uptime: time.Date(time.Now().Year(), time.January, 2, 3, 4, 4, 0, time.Local), lookback: "2s", @@ -130,9 +140,10 @@ func TestWatch(t *testing.T) { assert.NoError(t, err) w := NewSyslogWatcherOrDie(types.WatcherConfig{ - Plugin: "syslog", - LogPath: f.Name(), - Lookback: test.lookback, + Plugin: "syslog", + PluginConfig: getTestPluginConfig(), + LogPath: f.Name(), + Lookback: test.lookback, }) // Set the uptime. w.(*syslogWatcher).uptime = test.uptime diff --git a/pkg/kernelmonitor/logwatchers/types/log_watcher.go b/pkg/kernelmonitor/logwatchers/types/log_watcher.go index c916bbfe..c079c018 100644 --- a/pkg/kernelmonitor/logwatchers/types/log_watcher.go +++ b/pkg/kernelmonitor/logwatchers/types/log_watcher.go @@ -33,6 +33,9 @@ type WatcherConfig struct { // Plugin is the name of plugin which is currently used. // Currently supported: syslog, journald. Plugin string `json:"plugin, omitempty"` + // PluginConfig is a key/value configuration of a plugin. Valid configurations + // are defined in different log watcher plugin. + PluginConfig map[string]string `json:"pluginConfig, omitempty"` // LogPath is the path to the log LogPath string `json:"logPath, omitempty"` // Lookback is the time kernel watcher looks up From f16f0f630b57c5476fda79ca035244b37761010e Mon Sep 17 00:00:00 2001 From: Random-Liu Date: Fri, 3 Feb 2017 03:14:38 -0800 Subject: [PATCH 2/3] Rename helpers.go to translator.go --- .../logwatchers/syslog/{helpers.go => translator.go} | 0 .../logwatchers/syslog/{helpers_test.go => translator_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename pkg/kernelmonitor/logwatchers/syslog/{helpers.go => translator.go} (100%) rename pkg/kernelmonitor/logwatchers/syslog/{helpers_test.go => translator_test.go} (100%) diff --git a/pkg/kernelmonitor/logwatchers/syslog/helpers.go b/pkg/kernelmonitor/logwatchers/syslog/translator.go similarity index 100% rename from pkg/kernelmonitor/logwatchers/syslog/helpers.go rename to pkg/kernelmonitor/logwatchers/syslog/translator.go diff --git a/pkg/kernelmonitor/logwatchers/syslog/helpers_test.go b/pkg/kernelmonitor/logwatchers/syslog/translator_test.go similarity index 100% rename from pkg/kernelmonitor/logwatchers/syslog/helpers_test.go rename to pkg/kernelmonitor/logwatchers/syslog/translator_test.go From 01334e7c5e09dc650ec5e8ac1cce226f03d6e37d Mon Sep 17 00:00:00 2001 From: Random-Liu Date: Fri, 3 Feb 2017 03:30:48 -0800 Subject: [PATCH 3/3] Update Godeps --- Godeps/Godeps.json | 13 +- .../stretchr/testify/require/doc.go | 28 ++ .../testify/require/forward_requirements.go | 16 + .../stretchr/testify/require/require.go | 423 ++++++++++++++++++ .../stretchr/testify/require/require.go.tmpl | 6 + .../testify/require/require_forward.go | 347 ++++++++++++++ .../testify/require/require_forward.go.tmpl | 4 + .../stretchr/testify/require/requirements.go | 9 + 8 files changed, 842 insertions(+), 4 deletions(-) create mode 100644 vendor/github.com/stretchr/testify/require/doc.go create mode 100644 vendor/github.com/stretchr/testify/require/forward_requirements.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go create mode 100644 vendor/github.com/stretchr/testify/require/require.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go create mode 100644 vendor/github.com/stretchr/testify/require/require_forward.go.tmpl create mode 100644 vendor/github.com/stretchr/testify/require/requirements.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 76d53888..d4314cc9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -81,12 +81,12 @@ }, { "ImportPath": "github.com/docker/distribution/digest", - "Comment": "v2.4.0-rc.1-38-gcd27f17", + "Comment": "v2.4.0-rc.1-38-gcd27f179", "Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51" }, { "ImportPath": "github.com/docker/distribution/reference", - "Comment": "v2.4.0-rc.1-38-gcd27f17", + "Comment": "v2.4.0-rc.1-38-gcd27f179", "Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51" }, { @@ -110,12 +110,12 @@ }, { "ImportPath": "github.com/gogo/protobuf/proto", - "Comment": "v0.3-127-g8d70fb3", + "Comment": "v0.3-127-g8d70fb31", "Rev": "8d70fb3182befc465c4a1eac8ad4d38ff49778e2" }, { "ImportPath": "github.com/gogo/protobuf/sortkeys", - "Comment": "v0.3-127-g8d70fb3", + "Comment": "v0.3-127-g8d70fb31", "Rev": "8d70fb3182befc465c4a1eac8ad4d38ff49778e2" }, { @@ -171,6 +171,11 @@ "Comment": "v1.1.4-6-g18a02ba", "Rev": "18a02ba4a312f95da08ff4cfc0055750ce50ae9e" }, + { + "ImportPath": "github.com/stretchr/testify/require", + "Comment": "v1.1.4-6-g18a02ba", + "Rev": "18a02ba4a312f95da08ff4cfc0055750ce50ae9e" + }, { "ImportPath": "github.com/ugorji/go/codec", "Rev": "f4485b318aadd133842532f841dc205a8e339d74" diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go new file mode 100644 index 00000000..169de392 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -0,0 +1,28 @@ +// Package require implements the same assertions as the `assert` package but +// stops test execution when a test fails. +// +// Example Usage +// +// The following is a complete example using require in a standard test function: +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// require.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// Assertions +// +// The `require` package have same global functions as in the `assert` package, +// but instead of returning a boolean result they call `t.FailNow()`. +// +// 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. +package require diff --git a/vendor/github.com/stretchr/testify/require/forward_requirements.go b/vendor/github.com/stretchr/testify/require/forward_requirements.go new file mode 100644 index 00000000..d3c2ab9b --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/forward_requirements.go @@ -0,0 +1,16 @@ +package require + +// 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, + } +} + +//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go new file mode 100644 index 00000000..a0c40450 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -0,0 +1,423 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { + if !assert.Condition(t, comp, msgAndArgs...) { + t.FailNow() + } +} + +// Contains asserts that the specified string, list(array, slice...) or map 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'") +// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") +// +// Returns whether the assertion was successful (true) or not (false). +func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { + if !assert.Contains(t, s, contains, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.Empty(t, object, msgAndArgs...) { + t.FailNow() + } +} + +// 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 interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.Equal(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString, "An error was expected") +// +// Returns whether the assertion was successful (true) or not (false). +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { + if !assert.EqualError(t, theError, errString, msgAndArgs...) { + t.FailNow() + } +} + +// 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 interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.EqualValues(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.Error(t, err, msgAndArgs...) { + t.FailNow() + } +} + +// 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 interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.Exactly(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if !assert.Fail(t, failureMessage, msgAndArgs...) { + t.FailNow() + } +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { + if !assert.FailNow(t, failureMessage, msgAndArgs...) { + t.FailNow() + } +} + +// False asserts that the specified value is false. +// +// 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{}) { + if !assert.False(t, value, msgAndArgs...) { + t.FailNow() + } +} + +// 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 string, url string, values url.Values, str interface{}) { + if !assert.HTTPBodyContains(t, handler, method, url, values, str) { + t.FailNow() + } +} + +// 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 string, url string, values url.Values, str interface{}) { + if !assert.HTTPBodyNotContains(t, handler, method, url, values, str) { + t.FailNow() + } +} + +// 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 string, url string, values url.Values) { + if !assert.HTTPError(t, handler, method, url, values) { + t.FailNow() + } +} + +// 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 string, url string, values url.Values) { + if !assert.HTTPRedirect(t, handler, method, url, values) { + t.FailNow() + } +} + +// 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 string, url string, values url.Values) { + if !assert.HTTPSuccess(t, handler, method, url, values) { + t.FailNow() + } +} + +// 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{}) { + if !assert.Implements(t, interfaceObject, object, msgAndArgs...) { + t.FailNow() + } +} + +// 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 interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if !assert.InDelta(t, expected, actual, delta, msgAndArgs...) { + t.FailNow() + } +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + if !assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { + t.FailNow() + } +} + +// 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 interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if !assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { + t.FailNow() + } +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + if !assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { + t.FailNow() + } +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + if !assert.IsType(t, expectedType, object, msgAndArgs...) { + t.FailNow() + } +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// +// Returns whether the assertion was successful (true) or not (false). +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { + if !assert.JSONEq(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.Len(t, object, length, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.Nil(t, object, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.NoError(t, err, msgAndArgs...) { + t.FailNow() + } +} + +// NotContains asserts that the specified string, list(array, slice...) or map 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'") +// 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 interface{}, contains interface{}, msgAndArgs ...interface{}) { + if !assert.NotContains(t, s, contains, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.NotEmpty(t, object, msgAndArgs...) { + t.FailNow() + } +} + +// 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 interface{}, actual interface{}, msgAndArgs ...interface{}) { + if !assert.NotEqual(t, expected, actual, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.NotNil(t, object, msgAndArgs...) { + t.FailNow() + } +} + +// 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 assert.PanicTestFunc, msgAndArgs ...interface{}) { + if !assert.NotPanics(t, f, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.NotRegexp(t, rx, str, msgAndArgs...) { + t.FailNow() + } +} + +// NotZero asserts that i is not the zero value for its type and returns the truth. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if !assert.NotZero(t, i, msgAndArgs...) { + t.FailNow() + } +} + +// 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 assert.PanicTestFunc, msgAndArgs ...interface{}) { + if !assert.Panics(t, f, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.Regexp(t, rx, str, msgAndArgs...) { + t.FailNow() + } +} + +// 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{}) { + if !assert.True(t, value, msgAndArgs...) { + t.FailNow() + } +} + +// 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 time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + if !assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { + t.FailNow() + } +} + +// Zero asserts that i is the zero value for its type and returns the truth. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { + if !assert.Zero(t, i, msgAndArgs...) { + t.FailNow() + } +} diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl new file mode 100644 index 00000000..d2c38f6f --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -0,0 +1,6 @@ +{{.Comment}} +func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { + if !assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { + t.FailNow() + } +} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go new file mode 100644 index 00000000..83e9842e --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -0,0 +1,347 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package require + +import ( + assert "github.com/stretchr/testify/assert" + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { + Condition(a.t, comp, msgAndArgs...) +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") +// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") +// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { + Contains(a.t, s, contains, msgAndArgs...) +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { + Empty(a.t, object, msgAndArgs...) +} + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123, "123 and 123 should be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + Equal(a.t, expected, actual, 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() +// a.EqualError(err, expectedErrorString, "An error was expected") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { + EqualError(a.t, theError, errString, msgAndArgs...) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.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 interface{}, actual interface{}, msgAndArgs ...interface{}) { + EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err, "An error was expected") { +// assert.Equal(t, err, expectedError) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { + Error(a.t, err, msgAndArgs...) +} + +// Exactly asserts that two objects are equal is value and type. +// +// a.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 interface{}, actual interface{}, msgAndArgs ...interface{}) { + Exactly(a.t, expected, actual, msgAndArgs...) +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { + Fail(a.t, failureMessage, msgAndArgs...) +} + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { + FailNow(a.t, failureMessage, msgAndArgs...) +} + +// False asserts that the specified value is false. +// +// a.False(myBool, "myBool should be false") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { + False(a.t, value, msgAndArgs...) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(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 string, url string, values url.Values, str interface{}) { + HTTPBodyContains(a.t, handler, method, url, values, str) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(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 string, url string, values url.Values, str interface{}) { + HTTPBodyNotContains(a.t, handler, method, url, values, str) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.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 string, url string, values url.Values) { + HTTPError(a.t, handler, method, url, values) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.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 string, url string, values url.Values) { + HTTPRedirect(a.t, handler, method, url, values) +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.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 string, url string, values url.Values) { + HTTPSuccess(a.t, handler, method, url, values) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, (22 / 7.0), 0.01) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { + InDeltaSlice(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 interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { + InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { + IsType(a.t, expectedType, object, msgAndArgs...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { + JSONEq(a.t, expected, actual, msgAndArgs...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.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{}) { + Len(a.t, object, length, msgAndArgs...) +} + +// Nil asserts that the specified object is nil. +// +// a.Nil(err, "err should be nothing") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { + Nil(a.t, object, msgAndArgs...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, actualObj, expectedObj) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { + NoError(a.t, err, msgAndArgs...) +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") +// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") +// a.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 interface{}, contains interface{}, msgAndArgs ...interface{}) { + NotContains(a.t, s, contains, msgAndArgs...) +} + +// 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 a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { + NotEmpty(a.t, object, msgAndArgs...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2, "two objects shouldn't be equal") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err, "err should be something") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { + NotNil(a.t, object, msgAndArgs...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ +// RemainCalm() +// }, "Calling RemainCalm() should NOT panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + NotPanics(a.t, f, msgAndArgs...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^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{}) { + NotRegexp(a.t, rx, str, msgAndArgs...) +} + +// NotZero asserts that i is not the zero value for its type and returns the truth. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { + NotZero(a.t, i, msgAndArgs...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ +// GoCrazy() +// }, "Calling GoCrazy() should panic") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { + Panics(a.t, f, msgAndArgs...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("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{}) { + Regexp(a.t, rx, str, msgAndArgs...) +} + +// True asserts that the specified value is true. +// +// a.True(myBool, "myBool should be true") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { + True(a.t, value, msgAndArgs...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.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 time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { + WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// Zero asserts that i is the zero value for its type and returns the truth. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { + Zero(a.t, i, msgAndArgs...) +} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl new file mode 100644 index 00000000..b93569e0 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl @@ -0,0 +1,4 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { + {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go new file mode 100644 index 00000000..41147562 --- /dev/null +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -0,0 +1,9 @@ +package require + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) + FailNow() +} + +//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl