Merge pull request #88 from Random-Liu/add-arbitray-log-support

Add arbitray system log support
This commit is contained in:
Dawn Chen
2017-02-15 12:58:17 -08:00
committed by GitHub
20 changed files with 1207 additions and 202 deletions
+9 -4
View File
@@ -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"
+14
View File
@@ -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": []
}
+12
View File
@@ -0,0 +1,12 @@
{
"plugin": "journald",
"pluginConfig": {
"source": "docker"
},
"logPath": "/var/log/journal",
"lookback": "5m",
"bufferSize": 10,
"source": "docker-monitor",
"conditions": [],
"rules": []
}
+58
View File
@@ -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\\."
}
]
}
+3
View File
@@ -1,5 +1,8 @@
{
"plugin": "journald",
"pluginConfig": {
"source": "kernel"
},
"logPath": "/var/log/journal",
"lookback": "5m",
"bufferSize": 10,
@@ -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
}
@@ -1,102 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors 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 syslog
import (
"fmt"
"io"
"os"
"strings"
"time"
kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types"
"github.com/google/cadvisor/utils/tail"
)
// 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
}
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)
}
// 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)
}
// 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)
}
message := strings.Trim(line[loc+1:], " ")
return timestamp, message, 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
}
// 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
}
@@ -1,66 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors 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 syslog
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types"
)
func TestTranslate(t *testing.T) {
year := time.Now().Year()
testCases := []struct {
input string
err bool
log *kerntypes.KernelLog
}{
{
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",
},
},
{
// no log message
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: "",
},
},
{
// the right square bracket is missing
input: "May 21 12:23:45 hostname kernel: [9.999999 component: log message",
err: true,
},
}
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
}
assert.Equal(t, test.log, log)
}
}
@@ -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
}
@@ -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
@@ -0,0 +1,104 @@
/*
Copyright 2016 The Kubernetes Authors 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 syslog
import (
"fmt"
"regexp"
"time"
kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types"
"github.com/golang/glog"
)
// 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
}
// validatePluginConfig validates whether the plugin configuration.
func validatePluginConfig(cfg map[string]string) error {
if cfg[timestampKey] == "" {
return fmt.Errorf("unexpected empty timestamp regular expression")
}
if cfg[messageKey] == "" {
return fmt.Errorf("unexpected empty message regular expression")
}
if cfg[timestampFormatKey] == "" {
return fmt.Errorf("unexpected empty timestamp format string")
}
return nil
}
// 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)
}
return t
}
@@ -0,0 +1,92 @@
/*
Copyright 2016 The Kubernetes Authors 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 syslog
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types"
)
func TestTranslate(t *testing.T) {
year := time.Now().Year()
testCases := []struct {
config map[string]string
input string
err bool
log *kerntypes.KernelLog
}{
{
// 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",
},
},
{
// no log message
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: "",
},
},
{
// the right square bracket is missing
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)
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)
}
}
}
@@ -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
+28
View File
@@ -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
+16
View File
@@ -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
+423
View File
@@ -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()
}
}
+6
View File
@@ -0,0 +1,6 @@
{{.Comment}}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
if !assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) {
t.FailNow()
}
}
+347
View File
@@ -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...)
}
+4
View File
@@ -0,0 +1,4 @@
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}
+9
View File
@@ -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