Add node provider interfaace (#526)
This starts the work of having a `NodeProvider` which is responsible for providing node details. It splits the responsibilities of node management off to a new controller. The primary change here is to add the framework pieces for node management and move the VK CLI to use this new controller. It also adds support for node leases where available. This can be enabled via the command line (disabled by default), but may fall back if we find that leaess aren't supported on the cluster.
This commit is contained in:
311
vendor/gotest.tools/assert/assert.go
vendored
Normal file
311
vendor/gotest.tools/assert/assert.go
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
/*Package assert provides assertions for comparing expected values to actual
|
||||
values. When an assertion fails a helpful error message is printed.
|
||||
|
||||
Assert and Check
|
||||
|
||||
Assert() and Check() both accept a Comparison, and fail the test when the
|
||||
comparison fails. The one difference is that Assert() will end the test execution
|
||||
immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()),
|
||||
return the value of the comparison, then proceed with the rest of the test case.
|
||||
|
||||
Example usage
|
||||
|
||||
The example below shows assert used with some common types.
|
||||
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
)
|
||||
|
||||
func TestEverything(t *testing.T) {
|
||||
// booleans
|
||||
assert.Assert(t, ok)
|
||||
assert.Assert(t, !missing)
|
||||
|
||||
// primitives
|
||||
assert.Equal(t, count, 1)
|
||||
assert.Equal(t, msg, "the message")
|
||||
assert.Assert(t, total != 10) // NotEqual
|
||||
|
||||
// errors
|
||||
assert.NilError(t, closer.Close())
|
||||
assert.Error(t, err, "the exact error message")
|
||||
assert.ErrorContains(t, err, "includes this")
|
||||
assert.ErrorType(t, err, os.IsNotExist)
|
||||
|
||||
// complex types
|
||||
assert.DeepEqual(t, result, myStruct{Name: "title"})
|
||||
assert.Assert(t, is.Len(items, 3))
|
||||
assert.Assert(t, len(sequence) != 0) // NotEmpty
|
||||
assert.Assert(t, is.Contains(mapping, "key"))
|
||||
|
||||
// pointers and interface
|
||||
assert.Assert(t, is.Nil(ref))
|
||||
assert.Assert(t, ref != nil) // NotNil
|
||||
}
|
||||
|
||||
Comparisons
|
||||
|
||||
Package https://godoc.org/gotest.tools/assert/cmp provides
|
||||
many common comparisons. Additional comparisons can be written to compare
|
||||
values in other ways. See the example Assert (CustomComparison).
|
||||
|
||||
Automated migration from testify
|
||||
|
||||
gty-migrate-from-testify is a binary which can update source code which uses
|
||||
testify assertions to use the assertions provided by this package.
|
||||
|
||||
See http://bit.do/cmd-gty-migrate-from-testify.
|
||||
|
||||
|
||||
*/
|
||||
package assert // import "gotest.tools/assert"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
gocmp "github.com/google/go-cmp/cmp"
|
||||
"gotest.tools/assert/cmp"
|
||||
"gotest.tools/internal/format"
|
||||
"gotest.tools/internal/source"
|
||||
)
|
||||
|
||||
// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
|
||||
type BoolOrComparison interface{}
|
||||
|
||||
// TestingT is the subset of testing.T used by the assert package.
|
||||
type TestingT interface {
|
||||
FailNow()
|
||||
Fail()
|
||||
Log(args ...interface{})
|
||||
}
|
||||
|
||||
type helperT interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
const failureMessage = "assertion failed: "
|
||||
|
||||
// nolint: gocyclo
|
||||
func assert(
|
||||
t TestingT,
|
||||
failer func(),
|
||||
argSelector argSelector,
|
||||
comparison BoolOrComparison,
|
||||
msgAndArgs ...interface{},
|
||||
) bool {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
var success bool
|
||||
switch check := comparison.(type) {
|
||||
case bool:
|
||||
if check {
|
||||
return true
|
||||
}
|
||||
logFailureFromBool(t, msgAndArgs...)
|
||||
|
||||
// Undocumented legacy comparison without Result type
|
||||
case func() (success bool, message string):
|
||||
success = runCompareFunc(t, check, msgAndArgs...)
|
||||
|
||||
case nil:
|
||||
return true
|
||||
|
||||
case error:
|
||||
msg := "error is not nil: "
|
||||
t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...))
|
||||
|
||||
case cmp.Comparison:
|
||||
success = runComparison(t, argSelector, check, msgAndArgs...)
|
||||
|
||||
case func() cmp.Result:
|
||||
success = runComparison(t, argSelector, check, msgAndArgs...)
|
||||
|
||||
default:
|
||||
t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
|
||||
}
|
||||
|
||||
if success {
|
||||
return true
|
||||
}
|
||||
failer()
|
||||
return false
|
||||
}
|
||||
|
||||
func runCompareFunc(
|
||||
t TestingT,
|
||||
f func() (success bool, message string),
|
||||
msgAndArgs ...interface{},
|
||||
) bool {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
if success, message := f(); !success {
|
||||
t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool()
|
||||
const comparisonArgPos = 1
|
||||
args, err := source.CallExprArgs(stackIndex)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := boolFailureMessage(args[comparisonArgPos])
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
msg = "expression is false"
|
||||
}
|
||||
|
||||
t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
|
||||
}
|
||||
|
||||
func boolFailureMessage(expr ast.Expr) (string, error) {
|
||||
if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ {
|
||||
x, err := source.FormatNode(binaryExpr.X)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
y, err := source.FormatNode(binaryExpr.Y)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return x + " is " + y, nil
|
||||
}
|
||||
|
||||
if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT {
|
||||
x, err := source.FormatNode(unaryExpr.X)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return x + " is true", nil
|
||||
}
|
||||
|
||||
formatted, err := source.FormatNode(expr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "expression is false: " + formatted, nil
|
||||
}
|
||||
|
||||
// Assert performs a comparison. If the comparison fails the test is marked as
|
||||
// failed, a failure message is logged, and execution is stopped immediately.
|
||||
//
|
||||
// The comparison argument may be one of three types: bool, cmp.Comparison or
|
||||
// error.
|
||||
// When called with a bool the failure message will contain the literal source
|
||||
// code of the expression.
|
||||
// When called with a cmp.Comparison the comparison is responsible for producing
|
||||
// a helpful failure message.
|
||||
// When called with an error a nil value is considered success. A non-nil error
|
||||
// is a failure, and Error() is used as the failure message.
|
||||
func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Check performs a comparison. If the comparison fails the test is marked as
|
||||
// failed, a failure message is logged, and Check returns false. Otherwise returns
|
||||
// true.
|
||||
//
|
||||
// See Assert for details about the comparison arg and failure messages.
|
||||
func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...)
|
||||
}
|
||||
|
||||
// NilError fails the test immediately if err is not nil.
|
||||
// This is equivalent to Assert(t, err)
|
||||
func NilError(t TestingT, err error, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsAfterT, err, msgAndArgs...)
|
||||
}
|
||||
|
||||
// Equal uses the == operator to assert two values are equal and fails the test
|
||||
// if they are not equal.
|
||||
//
|
||||
// If the comparison fails Equal will use the variable names for x and y as part
|
||||
// of the failure message to identify the actual and expected values.
|
||||
//
|
||||
// If either x or y are a multi-line string the failure message will include a
|
||||
// unified diff of the two values. If the values only differ by whitespace
|
||||
// the unified diff will be augmented by replacing whitespace characters with
|
||||
// visible characters to identify the whitespace difference.
|
||||
//
|
||||
// This is equivalent to Assert(t, cmp.Equal(x, y)).
|
||||
func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...)
|
||||
}
|
||||
|
||||
// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are
|
||||
// equal and fails the test if they are not equal.
|
||||
//
|
||||
// Package https://godoc.org/gotest.tools/assert/opt provides some additional
|
||||
// commonly used Options.
|
||||
//
|
||||
// This is equivalent to Assert(t, cmp.DeepEqual(x, y)).
|
||||
func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...))
|
||||
}
|
||||
|
||||
// Error fails the test if err is nil, or the error message is not the expected
|
||||
// message.
|
||||
// Equivalent to Assert(t, cmp.Error(err, message)).
|
||||
func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...)
|
||||
}
|
||||
|
||||
// ErrorContains fails the test if err is nil, or the error message does not
|
||||
// contain the expected substring.
|
||||
// Equivalent to Assert(t, cmp.ErrorContains(err, substring)).
|
||||
func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...)
|
||||
}
|
||||
|
||||
// ErrorType fails the test if err is nil, or err is not the expected type.
|
||||
//
|
||||
// Expected can be one of:
|
||||
// a func(error) bool which returns true if the error is the expected type,
|
||||
// an instance of (or a pointer to) a struct of the expected type,
|
||||
// a pointer to an interface the error is expected to implement,
|
||||
// a reflect.Type of the expected struct or interface.
|
||||
//
|
||||
// Equivalent to Assert(t, cmp.ErrorType(err, expected)).
|
||||
func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...)
|
||||
}
|
||||
356
vendor/gotest.tools/assert/cmp/compare.go
vendored
Normal file
356
vendor/gotest.tools/assert/cmp/compare.go
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
/*Package cmp provides Comparisons for Assert and Check*/
|
||||
package cmp // import "gotest.tools/assert/cmp"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gotest.tools/internal/format"
|
||||
)
|
||||
|
||||
// Comparison is a function which compares values and returns ResultSuccess if
|
||||
// the actual value matches the expected value. If the values do not match the
|
||||
// Result will contain a message about why it failed.
|
||||
type Comparison func() Result
|
||||
|
||||
// DeepEqual compares two values using google/go-cmp (http://bit.do/go-cmp)
|
||||
// and succeeds if the values are equal.
|
||||
//
|
||||
// The comparison can be customized using comparison Options.
|
||||
// Package https://godoc.org/gotest.tools/assert/opt provides some additional
|
||||
// commonly used Options.
|
||||
func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
|
||||
return func() (result Result) {
|
||||
defer func() {
|
||||
if panicmsg, handled := handleCmpPanic(recover()); handled {
|
||||
result = ResultFailure(panicmsg)
|
||||
}
|
||||
}()
|
||||
diff := cmp.Diff(x, y, opts...)
|
||||
if diff == "" {
|
||||
return ResultSuccess
|
||||
}
|
||||
return multiLineDiffResult(diff)
|
||||
}
|
||||
}
|
||||
|
||||
func handleCmpPanic(r interface{}) (string, bool) {
|
||||
if r == nil {
|
||||
return "", false
|
||||
}
|
||||
panicmsg, ok := r.(string)
|
||||
if !ok {
|
||||
panic(r)
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(panicmsg, "cannot handle unexported field"):
|
||||
return panicmsg, true
|
||||
}
|
||||
panic(r)
|
||||
}
|
||||
|
||||
func toResult(success bool, msg string) Result {
|
||||
if success {
|
||||
return ResultSuccess
|
||||
}
|
||||
return ResultFailure(msg)
|
||||
}
|
||||
|
||||
// RegexOrPattern may be either a *regexp.Regexp or a string that is a valid
|
||||
// regexp pattern.
|
||||
type RegexOrPattern interface{}
|
||||
|
||||
// Regexp succeeds if value v matches regular expression re.
|
||||
//
|
||||
// Example:
|
||||
// assert.Assert(t, cmp.Regexp("^[0-9a-f]{32}$", str))
|
||||
// r := regexp.MustCompile("^[0-9a-f]{32}$")
|
||||
// assert.Assert(t, cmp.Regexp(r, str))
|
||||
func Regexp(re RegexOrPattern, v string) Comparison {
|
||||
match := func(re *regexp.Regexp) Result {
|
||||
return toResult(
|
||||
re.MatchString(v),
|
||||
fmt.Sprintf("value %q does not match regexp %q", v, re.String()))
|
||||
}
|
||||
|
||||
return func() Result {
|
||||
switch regex := re.(type) {
|
||||
case *regexp.Regexp:
|
||||
return match(regex)
|
||||
case string:
|
||||
re, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return ResultFailure(err.Error())
|
||||
}
|
||||
return match(re)
|
||||
default:
|
||||
return ResultFailure(fmt.Sprintf("invalid type %T for regex pattern", regex))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Equal succeeds if x == y. See assert.Equal for full documentation.
|
||||
func Equal(x, y interface{}) Comparison {
|
||||
return func() Result {
|
||||
switch {
|
||||
case x == y:
|
||||
return ResultSuccess
|
||||
case isMultiLineStringCompare(x, y):
|
||||
diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
|
||||
return multiLineDiffResult(diff)
|
||||
}
|
||||
return ResultFailureTemplate(`
|
||||
{{- .Data.x}} (
|
||||
{{- with callArg 0 }}{{ formatNode . }} {{end -}}
|
||||
{{- printf "%T" .Data.x -}}
|
||||
) != {{ .Data.y}} (
|
||||
{{- with callArg 1 }}{{ formatNode . }} {{end -}}
|
||||
{{- printf "%T" .Data.y -}}
|
||||
)`,
|
||||
map[string]interface{}{"x": x, "y": y})
|
||||
}
|
||||
}
|
||||
|
||||
func isMultiLineStringCompare(x, y interface{}) bool {
|
||||
strX, ok := x.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
strY, ok := y.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
|
||||
}
|
||||
|
||||
func multiLineDiffResult(diff string) Result {
|
||||
return ResultFailureTemplate(`
|
||||
--- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}}
|
||||
+++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}}
|
||||
{{ .Data.diff }}`,
|
||||
map[string]interface{}{"diff": diff})
|
||||
}
|
||||
|
||||
// Len succeeds if the sequence has the expected length.
|
||||
func Len(seq interface{}, expected int) Comparison {
|
||||
return func() (result Result) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
result = ResultFailure(fmt.Sprintf("type %T does not have a length", seq))
|
||||
}
|
||||
}()
|
||||
value := reflect.ValueOf(seq)
|
||||
length := value.Len()
|
||||
if length == expected {
|
||||
return ResultSuccess
|
||||
}
|
||||
msg := fmt.Sprintf("expected %s (length %d) to have length %d", seq, length, expected)
|
||||
return ResultFailure(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Contains succeeds if item is in collection. Collection may be a string, map,
|
||||
// slice, or array.
|
||||
//
|
||||
// If collection is a string, item must also be a string, and is compared using
|
||||
// strings.Contains().
|
||||
// If collection is a Map, contains will succeed if item is a key in the map.
|
||||
// If collection is a slice or array, item is compared to each item in the
|
||||
// sequence using reflect.DeepEqual().
|
||||
func Contains(collection interface{}, item interface{}) Comparison {
|
||||
return func() Result {
|
||||
colValue := reflect.ValueOf(collection)
|
||||
if !colValue.IsValid() {
|
||||
return ResultFailure(fmt.Sprintf("nil does not contain items"))
|
||||
}
|
||||
msg := fmt.Sprintf("%v does not contain %v", collection, item)
|
||||
|
||||
itemValue := reflect.ValueOf(item)
|
||||
switch colValue.Type().Kind() {
|
||||
case reflect.String:
|
||||
if itemValue.Type().Kind() != reflect.String {
|
||||
return ResultFailure("string may only contain strings")
|
||||
}
|
||||
return toResult(
|
||||
strings.Contains(colValue.String(), itemValue.String()),
|
||||
fmt.Sprintf("string %q does not contain %q", collection, item))
|
||||
|
||||
case reflect.Map:
|
||||
if itemValue.Type() != colValue.Type().Key() {
|
||||
return ResultFailure(fmt.Sprintf(
|
||||
"%v can not contain a %v key", colValue.Type(), itemValue.Type()))
|
||||
}
|
||||
return toResult(colValue.MapIndex(itemValue).IsValid(), msg)
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < colValue.Len(); i++ {
|
||||
if reflect.DeepEqual(colValue.Index(i).Interface(), item) {
|
||||
return ResultSuccess
|
||||
}
|
||||
}
|
||||
return ResultFailure(msg)
|
||||
default:
|
||||
return ResultFailure(fmt.Sprintf("type %T does not contain items", collection))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Panics succeeds if f() panics.
|
||||
func Panics(f func()) Comparison {
|
||||
return func() (result Result) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
result = ResultSuccess
|
||||
}
|
||||
}()
|
||||
f()
|
||||
return ResultFailure("did not panic")
|
||||
}
|
||||
}
|
||||
|
||||
// Error succeeds if err is a non-nil error, and the error message equals the
|
||||
// expected message.
|
||||
func Error(err error, message string) Comparison {
|
||||
return func() Result {
|
||||
switch {
|
||||
case err == nil:
|
||||
return ResultFailure("expected an error, got nil")
|
||||
case err.Error() != message:
|
||||
return ResultFailure(fmt.Sprintf(
|
||||
"expected error %q, got %s", message, formatErrorMessage(err)))
|
||||
}
|
||||
return ResultSuccess
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorContains succeeds if err is a non-nil error, and the error message contains
|
||||
// the expected substring.
|
||||
func ErrorContains(err error, substring string) Comparison {
|
||||
return func() Result {
|
||||
switch {
|
||||
case err == nil:
|
||||
return ResultFailure("expected an error, got nil")
|
||||
case !strings.Contains(err.Error(), substring):
|
||||
return ResultFailure(fmt.Sprintf(
|
||||
"expected error to contain %q, got %s", substring, formatErrorMessage(err)))
|
||||
}
|
||||
return ResultSuccess
|
||||
}
|
||||
}
|
||||
|
||||
func formatErrorMessage(err error) string {
|
||||
if _, ok := err.(interface {
|
||||
Cause() error
|
||||
}); ok {
|
||||
return fmt.Sprintf("%q\n%+v", err, err)
|
||||
}
|
||||
// This error was not wrapped with github.com/pkg/errors
|
||||
return fmt.Sprintf("%q", err)
|
||||
}
|
||||
|
||||
// Nil succeeds if obj is a nil interface, pointer, or function.
|
||||
//
|
||||
// Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices,
|
||||
// maps, and channels.
|
||||
func Nil(obj interface{}) Comparison {
|
||||
msgFunc := func(value reflect.Value) string {
|
||||
return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type())
|
||||
}
|
||||
return isNil(obj, msgFunc)
|
||||
}
|
||||
|
||||
func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison {
|
||||
return func() Result {
|
||||
if obj == nil {
|
||||
return ResultSuccess
|
||||
}
|
||||
value := reflect.ValueOf(obj)
|
||||
kind := value.Type().Kind()
|
||||
if kind >= reflect.Chan && kind <= reflect.Slice {
|
||||
if value.IsNil() {
|
||||
return ResultSuccess
|
||||
}
|
||||
return ResultFailure(msgFunc(value))
|
||||
}
|
||||
|
||||
return ResultFailure(fmt.Sprintf("%v (type %s) can not be nil", value, value.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorType succeeds if err is not nil and is of the expected type.
|
||||
//
|
||||
// Expected can be one of:
|
||||
// a func(error) bool which returns true if the error is the expected type,
|
||||
// an instance of (or a pointer to) a struct of the expected type,
|
||||
// a pointer to an interface the error is expected to implement,
|
||||
// a reflect.Type of the expected struct or interface.
|
||||
func ErrorType(err error, expected interface{}) Comparison {
|
||||
return func() Result {
|
||||
switch expectedType := expected.(type) {
|
||||
case func(error) bool:
|
||||
return cmpErrorTypeFunc(err, expectedType)
|
||||
case reflect.Type:
|
||||
if expectedType.Kind() == reflect.Interface {
|
||||
return cmpErrorTypeImplementsType(err, expectedType)
|
||||
}
|
||||
return cmpErrorTypeEqualType(err, expectedType)
|
||||
case nil:
|
||||
return ResultFailure(fmt.Sprintf("invalid type for expected: nil"))
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(expected)
|
||||
switch {
|
||||
case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType):
|
||||
return cmpErrorTypeEqualType(err, expectedType)
|
||||
case isPtrToInterface(expectedType):
|
||||
return cmpErrorTypeImplementsType(err, expectedType.Elem())
|
||||
}
|
||||
return ResultFailure(fmt.Sprintf("invalid type for expected: %T", expected))
|
||||
}
|
||||
}
|
||||
|
||||
func cmpErrorTypeFunc(err error, f func(error) bool) Result {
|
||||
if f(err) {
|
||||
return ResultSuccess
|
||||
}
|
||||
actual := "nil"
|
||||
if err != nil {
|
||||
actual = fmt.Sprintf("%s (%T)", err, err)
|
||||
}
|
||||
return ResultFailureTemplate(`error is {{ .Data.actual }}
|
||||
{{- with callArg 1 }}, not {{ formatNode . }}{{end -}}`,
|
||||
map[string]interface{}{"actual": actual})
|
||||
}
|
||||
|
||||
func cmpErrorTypeEqualType(err error, expectedType reflect.Type) Result {
|
||||
if err == nil {
|
||||
return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType))
|
||||
}
|
||||
errValue := reflect.ValueOf(err)
|
||||
if errValue.Type() == expectedType {
|
||||
return ResultSuccess
|
||||
}
|
||||
return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType))
|
||||
}
|
||||
|
||||
func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result {
|
||||
if err == nil {
|
||||
return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType))
|
||||
}
|
||||
errValue := reflect.ValueOf(err)
|
||||
if errValue.Type().Implements(expectedType) {
|
||||
return ResultSuccess
|
||||
}
|
||||
return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType))
|
||||
}
|
||||
|
||||
func isPtrToInterface(typ reflect.Type) bool {
|
||||
return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
func isPtrToStruct(typ reflect.Type) bool {
|
||||
return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct
|
||||
}
|
||||
100
vendor/gotest.tools/assert/cmp/result.go
vendored
Normal file
100
vendor/gotest.tools/assert/cmp/result.go
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package cmp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"text/template"
|
||||
|
||||
"gotest.tools/internal/source"
|
||||
)
|
||||
|
||||
// A Result of a Comparison.
|
||||
type Result interface {
|
||||
Success() bool
|
||||
}
|
||||
|
||||
// StringResult is an implementation of Result that reports the error message
|
||||
// string verbatim and does not provide any templating or formatting of the
|
||||
// message.
|
||||
type StringResult struct {
|
||||
success bool
|
||||
message string
|
||||
}
|
||||
|
||||
// Success returns true if the comparison was successful.
|
||||
func (r StringResult) Success() bool {
|
||||
return r.success
|
||||
}
|
||||
|
||||
// FailureMessage returns the message used to provide additional information
|
||||
// about the failure.
|
||||
func (r StringResult) FailureMessage() string {
|
||||
return r.message
|
||||
}
|
||||
|
||||
// ResultSuccess is a constant which is returned by a ComparisonWithResult to
|
||||
// indicate success.
|
||||
var ResultSuccess = StringResult{success: true}
|
||||
|
||||
// ResultFailure returns a failed Result with a failure message.
|
||||
func ResultFailure(message string) StringResult {
|
||||
return StringResult{message: message}
|
||||
}
|
||||
|
||||
// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure
|
||||
// is returned with the error message as the failure message.
|
||||
func ResultFromError(err error) Result {
|
||||
if err == nil {
|
||||
return ResultSuccess
|
||||
}
|
||||
return ResultFailure(err.Error())
|
||||
}
|
||||
|
||||
type templatedResult struct {
|
||||
success bool
|
||||
template string
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
func (r templatedResult) Success() bool {
|
||||
return r.success
|
||||
}
|
||||
|
||||
func (r templatedResult) FailureMessage(args []ast.Expr) string {
|
||||
msg, err := renderMessage(r, args)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("failed to render failure message: %s", err)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// ResultFailureTemplate returns a Result with a template string and data which
|
||||
// can be used to format a failure message. The template may access data from .Data,
|
||||
// the comparison args with the callArg function, and the formatNode function may
|
||||
// be used to format the call args.
|
||||
func ResultFailureTemplate(template string, data map[string]interface{}) Result {
|
||||
return templatedResult{template: template, data: data}
|
||||
}
|
||||
|
||||
func renderMessage(result templatedResult, args []ast.Expr) (string, error) {
|
||||
tmpl := template.New("failure").Funcs(template.FuncMap{
|
||||
"formatNode": source.FormatNode,
|
||||
"callArg": func(index int) ast.Expr {
|
||||
if index >= len(args) {
|
||||
return nil
|
||||
}
|
||||
return args[index]
|
||||
},
|
||||
})
|
||||
var err error
|
||||
tmpl, err = tmpl.Parse(result.template)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err = tmpl.Execute(buf, map[string]interface{}{
|
||||
"Data": result.data,
|
||||
})
|
||||
return buf.String(), err
|
||||
}
|
||||
106
vendor/gotest.tools/assert/result.go
vendored
Normal file
106
vendor/gotest.tools/assert/result.go
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package assert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"gotest.tools/assert/cmp"
|
||||
"gotest.tools/internal/format"
|
||||
"gotest.tools/internal/source"
|
||||
)
|
||||
|
||||
func runComparison(
|
||||
t TestingT,
|
||||
argSelector argSelector,
|
||||
f cmp.Comparison,
|
||||
msgAndArgs ...interface{},
|
||||
) bool {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
result := f()
|
||||
if result.Success() {
|
||||
return true
|
||||
}
|
||||
|
||||
var message string
|
||||
switch typed := result.(type) {
|
||||
case resultWithComparisonArgs:
|
||||
const stackIndex = 3 // Assert/Check, assert, runComparison
|
||||
args, err := source.CallExprArgs(stackIndex)
|
||||
if err != nil {
|
||||
t.Log(err.Error())
|
||||
}
|
||||
message = typed.FailureMessage(filterPrintableExpr(argSelector(args)))
|
||||
case resultBasic:
|
||||
message = typed.FailureMessage()
|
||||
default:
|
||||
message = fmt.Sprintf("comparison returned invalid Result type: %T", result)
|
||||
}
|
||||
|
||||
t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
|
||||
return false
|
||||
}
|
||||
|
||||
type resultWithComparisonArgs interface {
|
||||
FailureMessage(args []ast.Expr) string
|
||||
}
|
||||
|
||||
type resultBasic interface {
|
||||
FailureMessage() string
|
||||
}
|
||||
|
||||
// filterPrintableExpr filters the ast.Expr slice to only include Expr that are
|
||||
// easy to read when printed and contain relevant information to an assertion.
|
||||
//
|
||||
// Ident and SelectorExpr are included because they print nicely and the variable
|
||||
// names may provide additional context to their values.
|
||||
// BasicLit and CompositeLit are excluded because their source is equivalent to
|
||||
// their value, which is already available.
|
||||
// Other types are ignored for now, but could be added if they are relevant.
|
||||
func filterPrintableExpr(args []ast.Expr) []ast.Expr {
|
||||
result := make([]ast.Expr, len(args))
|
||||
for i, arg := range args {
|
||||
if isShortPrintableExpr(arg) {
|
||||
result[i] = arg
|
||||
continue
|
||||
}
|
||||
|
||||
if starExpr, ok := arg.(*ast.StarExpr); ok {
|
||||
result[i] = starExpr.X
|
||||
continue
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func isShortPrintableExpr(expr ast.Expr) bool {
|
||||
switch expr.(type) {
|
||||
case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr:
|
||||
return true
|
||||
case *ast.BinaryExpr, *ast.UnaryExpr:
|
||||
return true
|
||||
default:
|
||||
// CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
type argSelector func([]ast.Expr) []ast.Expr
|
||||
|
||||
func argsAfterT(args []ast.Expr) []ast.Expr {
|
||||
if len(args) < 1 {
|
||||
return nil
|
||||
}
|
||||
return args[1:]
|
||||
}
|
||||
|
||||
func argsFromComparisonCall(args []ast.Expr) []ast.Expr {
|
||||
if len(args) < 1 {
|
||||
return nil
|
||||
}
|
||||
if callExpr, ok := args[1].(*ast.CallExpr); ok {
|
||||
return callExpr.Args
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user