diff --git a/go.mod b/go.mod index 8b6beadfc..e37552938 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/google/go-containerregistry v0.19.1 github.com/moby/patternmatcher v0.6.0 github.com/spf13/cobra v1.8.0 - gotest.tools/v3 v3.0.3 + gotest.tools/v3 v3.5.1 ) require ( diff --git a/go.sum b/go.sum index dcb4e29fc..fe3a4c79c 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,6 @@ github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKs github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY= @@ -53,7 +52,6 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -66,7 +64,6 @@ github.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -82,19 +79,12 @@ github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -106,5 +96,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/vendor/gotest.tools/v3/assert/assert.go b/vendor/gotest.tools/v3/assert/assert.go index f3f805dde..c418bd07b 100644 --- a/vendor/gotest.tools/v3/assert/assert.go +++ b/vendor/gotest.tools/v3/assert/assert.go @@ -1,66 +1,94 @@ -/*Package assert provides assertions for comparing expected values to actual -values. When an assertion fails a helpful error message is printed. +/* +Package assert provides assertions for comparing expected values to actual +values in tests. When an assertion fails a helpful error message is printed. -Assert and Check +# Example usage -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. +All the assertions in this package use [testing.T.Helper] to mark themselves as +test helpers. This allows the testing package to print the filename and line +number of the file function that failed. -Example usage + assert.NilError(t, err) + // filename_test.go:212: assertion failed: error is not nil: file not found -The example below shows assert used with some common types. +If any assertion is called from a helper function, make sure to call t.Helper +from the helper function so that the filename and line number remain correct. +The examples below show assert used with some common types and the failure +messages it produces. The filename and line number portion of the failure +message is omitted from these examples for brevity. - import ( - "testing" + // booleans - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - ) + assert.Assert(t, ok) + // assertion failed: ok is false + assert.Assert(t, !missing) + // assertion failed: missing is true - func TestEverything(t *testing.T) { - // booleans - assert.Assert(t, ok) - assert.Assert(t, !missing) + // primitives - // primitives - assert.Equal(t, count, 1) - assert.Equal(t, msg, "the message") - assert.Assert(t, total != 10) // NotEqual + assert.Equal(t, count, 1) + // assertion failed: 0 (count int) != 1 (int) + assert.Equal(t, msg, "the message") + // assertion failed: my message (msg string) != the message (string) + assert.Assert(t, total != 10) // use Assert for NotEqual + // assertion failed: total is 10 + assert.Assert(t, count > 20, "count=%v", count) + // assertion failed: count is <= 20: count=1 - // 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) + // errors - // 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")) + assert.NilError(t, closer.Close()) + // assertion failed: error is not nil: close /file: errno 11 + assert.Error(t, err, "the exact error message") + // assertion failed: expected error "the exact error message", got "oops" + assert.ErrorContains(t, err, "includes this") + // assertion failed: expected error to contain "includes this", got "oops" + assert.ErrorIs(t, err, os.ErrNotExist) + // assertion failed: error is "oops", not "file does not exist" (os.ErrNotExist) - // pointers and interface - assert.Assert(t, is.Nil(ref)) - assert.Assert(t, ref != nil) // NotNil - } + // complex types + + assert.DeepEqual(t, result, myStruct{Name: "title"}) + // assertion failed: ... (diff of the two structs) + assert.Assert(t, is.Len(items, 3)) + // assertion failed: expected [] (length 0) to have length 3 + assert.Assert(t, len(sequence) != 0) // use Assert for NotEmpty + // assertion failed: len(sequence) is 0 + assert.Assert(t, is.Contains(mapping, "key")) + // assertion failed: map[other:1] does not contain key + + // pointers and interface + + assert.Assert(t, ref == nil) + // assertion failed: ref is not nil + assert.Assert(t, ref != nil) // use Assert for NotNil + // assertion failed: ref is nil -Comparisons +# Assert and Check -Package http://pkg.go.dev/gotest.tools/v3/assert/cmp provides +[Assert] and [Check] are very similar, they both accept a [cmp.Comparison], and fail +the test when the comparison fails. The one difference is that Assert uses +[testing.T.FailNow] to fail the test, which will end the test execution immediately. +Check uses [testing.T.Fail] to fail the test, which allows it to return the +result of the comparison, then proceed with the rest of the test case. + +Like [testing.T.FailNow], [Assert] must be called from the goroutine running the test, +not from other goroutines created during the test. [Check] is safe to use from any +goroutine. + +# Comparisons + +Package [gotest.tools/v3/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 +# Automated migration from testify gty-migrate-from-testify is a command which translates Go source code from testify assertions to the assertions provided by this package. See http://pkg.go.dev/gotest.tools/v3/assert/cmd/gty-migrate-from-testify. - - */ package assert // import "gotest.tools/v3/assert" @@ -70,10 +98,11 @@ import ( "gotest.tools/v3/internal/assert" ) -// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage. +// BoolOrComparison can be a bool, [cmp.Comparison], or error. See [Assert] for +// details about how this type is used. type BoolOrComparison interface{} -// TestingT is the subset of testing.T used by the assert package. +// TestingT is the subset of [testing.T] (see also [testing.TB]) used by the assert package. type TestingT interface { FailNow() Fail() @@ -88,16 +117,27 @@ type helperT interface { // failed, a failure message is logged, and execution is stopped immediately. // // The comparison argument may be one of three types: -// bool -// True is success. False is a failure. -// The failure message will contain the literal source code of the expression. -// cmp.Comparison -// Uses cmp.Result.Success() to check for success of failure. -// The comparison is responsible for producing a helpful failure message. -// http://pkg.go.dev/gotest.tools/v3/assert/cmp provides many common comparisons. -// error -// A nil value is considered success. -// A non-nil error is a failure, err.Error() is used as the failure message. +// +// bool +// True is success. False is a failure. The failure message will contain +// the literal source code of the expression. +// +// cmp.Comparison +// Uses cmp.Result.Success() to check for success or failure. +// The comparison is responsible for producing a helpful failure message. +// http://pkg.go.dev/gotest.tools/v3/assert/cmp provides many common comparisons. +// +// error +// A nil value is considered success, and a non-nil error is a failure. +// The return value of error.Error is used as the failure message. +// +// Extra details can be added to the failure message using msgAndArgs. msgAndArgs +// may be either a single string, or a format string and args that will be +// passed to [fmt.Sprintf]. +// +// Assert uses [testing.TB.FailNow] to fail the test. Like t.FailNow, Assert must be called +// from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] from other goroutines. func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -108,10 +148,10 @@ func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) } // 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. +// failed, a failure message is printed, and Check returns false. If the comparison +// is successful Check returns true. Check may be called from any goroutine. // -// See Assert for details about the comparison arg and failure messages. +// 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() @@ -123,8 +163,12 @@ func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) b return true } -// NilError fails the test immediately if err is not nil. -// This is equivalent to Assert(t, err) +// NilError fails the test immediately if err is not nil, and includes err.Error +// in the failure message. +// +// NilError uses [testing.TB.FailNow] to fail the test. Like t.FailNow, NilError must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] from other goroutines. func NilError(t TestingT, err error, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -137,15 +181,22 @@ func NilError(t TestingT, err error, msgAndArgs ...interface{}) { // 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 the comparison fails Equal will use the variable names and types of +// x and y as part of the failure message to identify the actual and expected +// values. +// +// assert.Equal(t, actual, expected) +// // main_test.go:41: assertion failed: 1 (actual int) != 21 (expected int32) // // 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)). +// Equal uses [testing.T.FailNow] to fail the test. Like t.FailNow, Equal must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] with [cmp.Equal] from other +// goroutines. func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -155,13 +206,16 @@ func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { } } -// DeepEqual uses google/go-cmp (https://godoc.org/github.com/google/go-cmp/cmp) +// DeepEqual uses [github.com/google/go-cmp/cmp] // to assert two values are equal and fails the test if they are not equal. // -// Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional +// Package [gotest.tools/v3/assert/opt] provides some additional // commonly used Options. // -// This is equivalent to Assert(t, cmp.DeepEqual(x, y)). +// DeepEqual uses [testing.T.FailNow] to fail the test. Like t.FailNow, DeepEqual must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] with [cmp.DeepEqual] from other +// goroutines. func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -171,21 +225,33 @@ func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { } } -// 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{}) { +// Error fails the test if err is nil, or if err.Error is not equal to expected. +// Both err.Error and expected will be included in the failure message. +// Error performs an exact match of the error text. Use [ErrorContains] if only +// part of the error message is relevant. Use [ErrorType] or [ErrorIs] to compare +// errors by type. +// +// Error uses [testing.T.FailNow] to fail the test. Like t.FailNow, Error must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] with [cmp.Error] from other +// goroutines. +func Error(t TestingT, err error, expected string, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() } - if !assert.Eval(t, assert.ArgsAfterT, cmp.Error(err, message), msgAndArgs...) { + if !assert.Eval(t, assert.ArgsAfterT, cmp.Error(err, expected), msgAndArgs...) { t.FailNow() } } -// 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)). +// ErrorContains fails the test if err is nil, or if err.Error does not +// contain the expected substring. Both err.Error and the expected substring +// will be included in the failure message. +// +// ErrorContains uses [testing.T.FailNow] to fail the test. Like t.FailNow, ErrorContains +// must be called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] with [cmp.ErrorContains] from other +// goroutines. func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -196,19 +262,30 @@ func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interf } // ErrorType fails the test if err is nil, or err is not the expected type. -// Equivalent to Assert(t, cmp.ErrorType(err, expected)). +// New code should use ErrorIs instead. // // Expected can be one of: -// func(error) bool -// Function should return true if the error is the expected type. -// type struct{}, type &struct{} -// A struct or a pointer to a struct. -// Fails if the error is not of the same type as expected. -// type &interface{} -// A pointer to an interface type. -// Fails if err does not implement the interface. -// reflect.Type -// Fails if err does not implement the reflect.Type +// +// func(error) bool +// The function should return true if the error is the expected type. +// +// struct{} or *struct{} +// A struct or a pointer to a struct. The assertion fails if the error is +// not of the same type. +// +// *interface{} +// A pointer to an interface type. The assertion fails if err does not +// implement the interface. +// +// reflect.Type +// The assertion fails if err does not implement the reflect.Type. +// +// ErrorType uses [testing.T.FailNow] to fail the test. Like t.FailNow, ErrorType +// must be called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] with [cmp.ErrorType] from other +// goroutines. +// +// Deprecated: Use [ErrorIs] func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -217,3 +294,20 @@ func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interf t.FailNow() } } + +// ErrorIs fails the test if err is nil, or the error does not match expected +// when compared using errors.Is. See [errors.Is] for +// accepted arguments. +// +// ErrorIs uses [testing.T.FailNow] to fail the test. Like t.FailNow, ErrorIs +// must be called from the goroutine running the test function, not from other +// goroutines created during the test. Use [Check] with [cmp.ErrorIs] from other +// goroutines. +func ErrorIs(t TestingT, err error, expected error, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorIs(err, expected), msgAndArgs...) { + t.FailNow() + } +} diff --git a/vendor/gotest.tools/v3/assert/cmp/compare.go b/vendor/gotest.tools/v3/assert/cmp/compare.go index 3c0e05ab5..118844f35 100644 --- a/vendor/gotest.tools/v3/assert/cmp/compare.go +++ b/vendor/gotest.tools/v3/assert/cmp/compare.go @@ -2,6 +2,7 @@ package cmp // import "gotest.tools/v3/assert/cmp" import ( + "errors" "fmt" "reflect" "regexp" @@ -11,17 +12,16 @@ import ( "gotest.tools/v3/internal/format" ) -// Comparison is a function which compares values and returns ResultSuccess if +// 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. +// [Result] will contain a message about why it failed. type Comparison func() Result -// DeepEqual compares two values using google/go-cmp -// (https://godoc.org/github.com/google/go-cmp/cmp) +// DeepEqual compares two values using [github.com/google/go-cmp/cmp] // and succeeds if the values are equal. // // The comparison can be customized using comparison Options. -// Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional +// Package [gotest.tools/v3/assert/opt] provides some additional // commonly used Options. func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { return func() (result Result) { @@ -34,7 +34,7 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { if diff == "" { return ResultSuccess } - return multiLineDiffResult(diff) + return multiLineDiffResult(diff, x, y) } } @@ -60,16 +60,17 @@ func toResult(success bool, msg string) Result { return ResultFailure(msg) } -// RegexOrPattern may be either a *regexp.Regexp or a string that is a valid +// 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)) +// +// 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( @@ -93,7 +94,7 @@ func Regexp(re RegexOrPattern, v string) Comparison { } } -// Equal succeeds if x == y. See assert.Equal for full documentation. +// Equal succeeds if x == y. See [gotest.tools/v3/assert.Equal] for full documentation. func Equal(x, y interface{}) Comparison { return func() Result { switch { @@ -101,7 +102,7 @@ func Equal(x, y interface{}) Comparison { return ResultSuccess case isMultiLineStringCompare(x, y): diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)}) - return multiLineDiffResult(diff) + return multiLineDiffResult(diff, x, y) } return ResultFailureTemplate(` {{- printf "%v" .Data.x}} ( @@ -127,12 +128,12 @@ func isMultiLineStringCompare(x, y interface{}) bool { return strings.Contains(strX, "\n") || strings.Contains(strY, "\n") } -func multiLineDiffResult(diff string) Result { +func multiLineDiffResult(diff string, x, y interface{}) Result { return ResultFailureTemplate(` --- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}} +++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}} {{ .Data.diff }}`, - map[string]interface{}{"diff": diff}) + map[string]interface{}{"diff": diff, "x": x, "y": y}) } // Len succeeds if the sequence has the expected length. @@ -157,15 +158,15 @@ func Len(seq interface{}, expected int) Comparison { // slice, or array. // // If collection is a string, item must also be a string, and is compared using -// strings.Contains(). +// [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(). +// 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")) + return ResultFailure("nil does not contain items") } msg := fmt.Sprintf("%v does not contain %v", collection, item) @@ -247,6 +248,7 @@ type causer interface { } func formatErrorMessage(err error) string { + //nolint:errorlint // unwrapping is not appropriate here if _, ok := err.(causer); ok { return fmt.Sprintf("%q\n%+v", err, err) } @@ -256,7 +258,7 @@ func formatErrorMessage(err error) string { // Nil succeeds if obj is a nil interface, pointer, or function. // -// Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices, +// Use [gotest.tools/v3/assert.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 { @@ -286,16 +288,26 @@ func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison { // ErrorType succeeds if err is not nil and is of the expected type. // // Expected can be one of: -// func(error) bool +// +// func(error) bool +// // Function should return true if the error is the expected type. -// type struct{}, type &struct{} +// +// type struct{}, type &struct{} +// // A struct or a pointer to a struct. // Fails if the error is not of the same type as expected. -// type &interface{} +// +// type &interface{} +// // A pointer to an interface type. // Fails if err does not implement the interface. -// reflect.Type -// Fails if err does not implement the reflect.Type +// +// reflect.Type +// +// Fails if err does not implement the [reflect.Type]. +// +// Deprecated: Use [ErrorIs] func ErrorType(err error, expected interface{}) Comparison { return func() Result { switch expectedType := expected.(type) { @@ -307,7 +319,7 @@ func ErrorType(err error, expected interface{}) Comparison { } return cmpErrorTypeEqualType(err, expectedType) case nil: - return ResultFailure(fmt.Sprintf("invalid type for expected: nil")) + return ResultFailure("invalid type for expected: nil") } expectedType := reflect.TypeOf(expected) @@ -363,3 +375,30 @@ func isPtrToInterface(typ reflect.Type) bool { func isPtrToStruct(typ reflect.Type) bool { return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct } + +var ( + stdlibErrorNewType = reflect.TypeOf(errors.New("")) + stdlibFmtErrorType = reflect.TypeOf(fmt.Errorf("%w", fmt.Errorf(""))) +) + +// ErrorIs succeeds if errors.Is(actual, expected) returns true. See +// [errors.Is] for accepted argument values. +func ErrorIs(actual error, expected error) Comparison { + return func() Result { + if errors.Is(actual, expected) { + return ResultSuccess + } + + // The type of stdlib errors is excluded because the type is not relevant + // in those cases. The type is only important when it is a user defined + // custom error type. + return ResultFailureTemplate(`error is + {{- if not .Data.a }} nil,{{ else }} + {{- printf " \"%v\"" .Data.a }} + {{- if notStdlibErrorType .Data.a }} ({{ printf "%T" .Data.a }}){{ end }}, + {{- end }} not {{ printf "\"%v\"" .Data.x }} ( + {{- with callArg 1 }}{{ formatNode . }}{{ end }} + {{- if notStdlibErrorType .Data.x }}{{ printf " %T" .Data.x }}{{ end }})`, + map[string]interface{}{"a": actual, "x": expected}) + } +} diff --git a/vendor/gotest.tools/v3/assert/cmp/result.go b/vendor/gotest.tools/v3/assert/cmp/result.go index 3b48d9bf0..9992ede54 100644 --- a/vendor/gotest.tools/v3/assert/cmp/result.go +++ b/vendor/gotest.tools/v3/assert/cmp/result.go @@ -4,17 +4,18 @@ import ( "bytes" "fmt" "go/ast" + "reflect" "text/template" "gotest.tools/v3/internal/source" ) -// A Result of a Comparison. +// A Result of a [Comparison]. type Result interface { Success() bool } -// StringResult is an implementation of Result that reports the error message +// 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 { @@ -33,16 +34,16 @@ func (r StringResult) FailureMessage() string { return r.message } -// ResultSuccess is a constant which is returned by a ComparisonWithResult to +// ResultSuccess is a constant which is returned by a [Comparison] to // indicate success. var ResultSuccess = StringResult{success: true} -// ResultFailure returns a failed Result with a failure message. +// 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 +// 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 { @@ -68,7 +69,12 @@ func (r templatedResult) FailureMessage(args []ast.Expr) string { return msg } -// ResultFailureTemplate returns a Result with a template string and data which +func (r templatedResult) UpdatedExpected(stackIndex int) error { + // TODO: would be nice to have structured data instead of a map + return source.UpdateExpectedValue(stackIndex+1, r.data["x"], r.data["y"]) +} + +// 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. @@ -85,6 +91,11 @@ func renderMessage(result templatedResult, args []ast.Expr) (string, error) { } return args[index] }, + // TODO: any way to include this from ErrorIS instead of here? + "notStdlibErrorType": func(typ interface{}) bool { + r := reflect.TypeOf(typ) + return r != stdlibFmtErrorType && r != stdlibErrorNewType + }, }) var err error tmpl, err = tmpl.Parse(result.template) diff --git a/vendor/gotest.tools/v3/env/env.go b/vendor/gotest.tools/v3/env/env.go index 7a71acd0d..9653cf187 100644 --- a/vendor/gotest.tools/v3/env/env.go +++ b/vendor/gotest.tools/v3/env/env.go @@ -1,4 +1,5 @@ -/*Package env provides functions to test code that read environment variables +/* +Package env provides functions to test code that read environment variables or the current working directory. */ package env // import "gotest.tools/v3/env" @@ -21,6 +22,8 @@ type helperT interface { // // When used with Go 1.14+ the unpatch function will be called automatically // when the test ends, unless the TEST_NOCLEANUP env var is set to true. +// +// Deprecated: use t.SetEnv func Patch(t assert.TestingT, key, value string) func() { if ht, ok := t.(helperT); ok { ht.Helper() @@ -69,7 +72,7 @@ func PatchAll(t assert.TestingT, env map[string]string) func() { return clean } -// ToMap takes a list of strings in the format returned by os.Environ() and +// ToMap takes a list of strings in the format returned by [os.Environ] and // returns a mapping of keys to values. func ToMap(env []string) map[string]string { result := map[string]string{} diff --git a/vendor/gotest.tools/v3/fs/file.go b/vendor/gotest.tools/v3/fs/file.go index de5f4ec12..f778e9c88 100644 --- a/vendor/gotest.tools/v3/fs/file.go +++ b/vendor/gotest.tools/v3/fs/file.go @@ -1,10 +1,10 @@ -/*Package fs provides tools for creating temporary files, and testing the +/* +Package fs provides tools for creating temporary files, and testing the contents and structure of a directory. */ package fs // import "gotest.tools/v3/fs" import ( - "io/ioutil" "os" "path/filepath" "runtime" @@ -45,7 +45,7 @@ func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File { if ht, ok := t.(helperT); ok { ht.Helper() } - tempfile, err := ioutil.TempFile("", cleanPrefix(prefix)+"-") + tempfile, err := os.CreateTemp("", cleanPrefix(prefix)+"-") assert.NilError(t, err) file := &File{path: tempfile.Name()} @@ -71,8 +71,7 @@ func (f *File) Path() string { // Remove the file func (f *File) Remove() { - // nolint: errcheck - os.Remove(f.path) + _ = os.Remove(f.path) } // Dir is a temporary directory @@ -89,7 +88,7 @@ func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir { if ht, ok := t.(helperT); ok { ht.Helper() } - path, err := ioutil.TempDir("", cleanPrefix(prefix)+"-") + path, err := os.MkdirTemp("", cleanPrefix(prefix)+"-") assert.NilError(t, err) dir := &Dir{path: path} cleanup.Cleanup(t, dir.Remove) @@ -105,11 +104,26 @@ func (d *Dir) Path() string { // Remove the directory func (d *Dir) Remove() { - // nolint: errcheck - os.RemoveAll(d.path) + _ = os.RemoveAll(d.path) } // Join returns a new path with this directory as the base of the path func (d *Dir) Join(parts ...string) string { return filepath.Join(append([]string{d.Path()}, parts...)...) } + +// DirFromPath returns a Dir for a path that already exists. No directory is created. +// Unlike NewDir the directory will not be removed automatically when the test exits, +// it is the callers responsibly to remove the directory. +// DirFromPath can be used with Apply to modify an existing directory. +// +// If the path does not already exist, use NewDir instead. +func DirFromPath(t assert.TestingT, path string, ops ...PathOp) *Dir { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + dir := &Dir{path: path} + assert.NilError(t, applyPathOps(dir, ops)) + return dir +} diff --git a/vendor/gotest.tools/v3/fs/manifest.go b/vendor/gotest.tools/v3/fs/manifest.go index d39675637..d3ddcaca8 100644 --- a/vendor/gotest.tools/v3/fs/manifest.go +++ b/vendor/gotest.tools/v3/fs/manifest.go @@ -1,12 +1,11 @@ package fs import ( + "fmt" "io" - "io/ioutil" "os" "path/filepath" - "github.com/pkg/errors" "gotest.tools/v3/assert" ) @@ -56,9 +55,9 @@ type dirEntry interface { Type() string } -// ManifestFromDir creates a Manifest by reading the directory at path. The +// ManifestFromDir creates a [Manifest] by reading the directory at path. The // manifest stores the structure and properties of files in the directory. -// ManifestFromDir can be used with Equal to compare two directories. +// ManifestFromDir can be used with [Equal] to compare two directories. func ManifestFromDir(t assert.TestingT, path string) Manifest { if ht, ok := t.(helperT); ok { ht.Helper() @@ -75,7 +74,7 @@ func manifestFromDir(path string) (Manifest, error) { case err != nil: return Manifest{}, err case !info.IsDir(): - return Manifest{}, errors.Errorf("path %s must be a directory", path) + return Manifest{}, fmt.Errorf("path %s must be a directory", path) } directory, err := newDirectory(path, info) @@ -84,7 +83,7 @@ func manifestFromDir(path string) (Manifest, error) { func newDirectory(path string, info os.FileInfo) (*directory, error) { items := make(map[string]dirEntry) - children, err := ioutil.ReadDir(path) + children, err := os.ReadDir(path) if err != nil { return nil, err } @@ -103,7 +102,11 @@ func newDirectory(path string, info os.FileInfo) (*directory, error) { }, nil } -func getTypedResource(path string, info os.FileInfo) (dirEntry, error) { +func getTypedResource(path string, entry os.DirEntry) (dirEntry, error) { + info, err := entry.Info() + if err != nil { + return nil, err + } switch { case info.IsDir(): return newDirectory(path, info) diff --git a/vendor/gotest.tools/v3/fs/manifest_unix.go b/vendor/gotest.tools/v3/fs/manifest_unix.go index bba2fcd9d..d2956f33d 100644 --- a/vendor/gotest.tools/v3/fs/manifest_unix.go +++ b/vendor/gotest.tools/v3/fs/manifest_unix.go @@ -1,16 +1,24 @@ +//go:build !windows // +build !windows package fs import ( "os" + "runtime" "syscall" ) -const ( - defaultRootDirMode = os.ModeDir | 0700 - defaultSymlinkMode = os.ModeSymlink | 0777 -) +const defaultRootDirMode = os.ModeDir | 0700 + +var defaultSymlinkMode = os.ModeSymlink | 0777 + +func init() { + switch runtime.GOOS { + case "darwin": + defaultSymlinkMode = os.ModeSymlink | 0755 + } +} func newResourceFromInfo(info os.FileInfo) resource { statT := info.Sys().(*syscall.Stat_t) diff --git a/vendor/gotest.tools/v3/fs/ops.go b/vendor/gotest.tools/v3/fs/ops.go index 4589623fa..9d86a696e 100644 --- a/vendor/gotest.tools/v3/fs/ops.go +++ b/vendor/gotest.tools/v3/fs/ops.go @@ -2,22 +2,21 @@ package fs import ( "bytes" + "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" "time" - "github.com/pkg/errors" "gotest.tools/v3/assert" ) const defaultFileMode = 0644 -// PathOp is a function which accepts a Path and performs an operation on that -// path. When called with real filesystem objects (File or Dir) a PathOp modifies -// the filesystem at the path. When used with a Manifest object a PathOp updates +// PathOp is a function which accepts a [Path] and performs an operation on that +// path. When called with real filesystem objects ([File] or [Dir]) a PathOp modifies +// the filesystem at the path. When used with a [Manifest] object a PathOp updates // the manifest to expect a value. type PathOp func(path Path) error @@ -39,33 +38,33 @@ type manifestDirectory interface { AddDirectory(path string, ops ...PathOp) error } -// WithContent writes content to a file at Path +// WithContent writes content to a file at [Path] func WithContent(content string) PathOp { return func(path Path) error { if m, ok := path.(manifestFile); ok { - m.SetContent(ioutil.NopCloser(strings.NewReader(content))) + m.SetContent(io.NopCloser(strings.NewReader(content))) return nil } - return ioutil.WriteFile(path.Path(), []byte(content), defaultFileMode) + return os.WriteFile(path.Path(), []byte(content), defaultFileMode) } } -// WithBytes write bytes to a file at Path +// WithBytes write bytes to a file at [Path] func WithBytes(raw []byte) PathOp { return func(path Path) error { if m, ok := path.(manifestFile); ok { - m.SetContent(ioutil.NopCloser(bytes.NewReader(raw))) + m.SetContent(io.NopCloser(bytes.NewReader(raw))) return nil } - return ioutil.WriteFile(path.Path(), raw, defaultFileMode) + return os.WriteFile(path.Path(), raw, defaultFileMode) } } -// WithReaderContent copies the reader contents to the file at Path +// WithReaderContent copies the reader contents to the file at [Path] func WithReaderContent(r io.Reader) PathOp { return func(path Path) error { if m, ok := path.(manifestFile); ok { - m.SetContent(ioutil.NopCloser(r)) + m.SetContent(io.NopCloser(r)) return nil } f, err := os.OpenFile(path.Path(), os.O_WRONLY, defaultFileMode) @@ -78,7 +77,7 @@ func WithReaderContent(r io.Reader) PathOp { } } -// AsUser changes ownership of the file system object at Path +// AsUser changes ownership of the file system object at [Path] func AsUser(uid, gid int) PathOp { return func(path Path) error { if m, ok := path.(manifestResource); ok { @@ -107,7 +106,7 @@ func WithFile(filename, content string, ops ...PathOp) PathOp { } func createFile(fullpath string, content string) error { - return ioutil.WriteFile(fullpath, []byte(content), defaultFileMode) + return os.WriteFile(fullpath, []byte(content), defaultFileMode) } // WithFiles creates all the files in the directory at path with their content @@ -133,17 +132,17 @@ func WithFiles(files map[string]string) PathOp { } } -// FromDir copies the directory tree from the source path into the new Dir +// FromDir copies the directory tree from the source path into the new [Dir] func FromDir(source string) PathOp { return func(path Path) error { if _, ok := path.(manifestDirectory); ok { - return errors.New("use manifest.FromDir") + return fmt.Errorf("use manifest.FromDir") } return copyDirectory(source, path.Path()) } } -// WithDir creates a subdirectory in the directory at path. Additional PathOp +// WithDir creates a subdirectory in the directory at path. Additional [PathOp] // can be used to modify the subdirectory func WithDir(name string, ops ...PathOp) PathOp { const defaultMode = 0755 @@ -162,7 +161,7 @@ func WithDir(name string, ops ...PathOp) PathOp { } } -// Apply the PathOps to the File +// Apply the PathOps to the [File] func Apply(t assert.TestingT, path Path, ops ...PathOp) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -179,7 +178,7 @@ func applyPathOps(path Path, ops []PathOp) error { return nil } -// WithMode sets the file mode on the directory or file at path +// WithMode sets the file mode on the directory or file at [Path] func WithMode(mode os.FileMode) PathOp { return func(path Path) error { if m, ok := path.(manifestResource); ok { @@ -191,34 +190,38 @@ func WithMode(mode os.FileMode) PathOp { } func copyDirectory(source, dest string) error { - entries, err := ioutil.ReadDir(source) + entries, err := os.ReadDir(source) if err != nil { return err } for _, entry := range entries { sourcePath := filepath.Join(source, entry.Name()) destPath := filepath.Join(dest, entry.Name()) - switch { - case entry.IsDir(): - if err := os.Mkdir(destPath, 0755); err != nil { - return err - } - if err := copyDirectory(sourcePath, destPath); err != nil { - return err - } - case entry.Mode()&os.ModeSymlink != 0: - if err := copySymLink(sourcePath, destPath); err != nil { - return err - } - default: - if err := copyFile(sourcePath, destPath); err != nil { - return err - } + err = copyEntry(entry, destPath, sourcePath) + if err != nil { + return err } } return nil } +func copyEntry(entry os.DirEntry, destPath string, sourcePath string) error { + if entry.IsDir() { + if err := os.Mkdir(destPath, 0755); err != nil { + return err + } + return copyDirectory(sourcePath, destPath) + } + info, err := entry.Info() + if err != nil { + return err + } + if info.Mode()&os.ModeSymlink != 0 { + return copySymLink(sourcePath, destPath) + } + return copyFile(sourcePath, destPath) +} + func copySymLink(source, dest string) error { link, err := os.Readlink(source) if err != nil { @@ -228,17 +231,17 @@ func copySymLink(source, dest string) error { } func copyFile(source, dest string) error { - content, err := ioutil.ReadFile(source) + content, err := os.ReadFile(source) if err != nil { return err } - return ioutil.WriteFile(dest, content, 0644) + return os.WriteFile(dest, content, 0644) } // WithSymlink creates a symlink in the directory which links to target. // Target must be a path relative to the directory. // -// Note: the argument order is the inverse of os.Symlink to be consistent with +// Note: the argument order is the inverse of [os.Symlink] to be consistent with // the other functions in this package. func WithSymlink(path, target string) PathOp { return func(root Path) error { @@ -252,12 +255,12 @@ func WithSymlink(path, target string) PathOp { // WithHardlink creates a link in the directory which links to target. // Target must be a path relative to the directory. // -// Note: the argument order is the inverse of os.Link to be consistent with +// Note: the argument order is the inverse of [os.Link] to be consistent with // the other functions in this package. func WithHardlink(path, target string) PathOp { return func(root Path) error { if _, ok := root.(manifestDirectory); ok { - return errors.New("WithHardlink not implemented for manifests") + return fmt.Errorf("WithHardlink not implemented for manifests") } return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) } @@ -268,7 +271,7 @@ func WithHardlink(path, target string) PathOp { func WithTimestamps(atime, mtime time.Time) PathOp { return func(root Path) error { if _, ok := root.(manifestDirectory); ok { - return errors.New("WithTimestamp not implemented for manifests") + return fmt.Errorf("WithTimestamp not implemented for manifests") } return os.Chtimes(root.Path(), atime, mtime) } diff --git a/vendor/gotest.tools/v3/fs/path.go b/vendor/gotest.tools/v3/fs/path.go index c301b9048..8f3bc922a 100644 --- a/vendor/gotest.tools/v3/fs/path.go +++ b/vendor/gotest.tools/v3/fs/path.go @@ -3,7 +3,6 @@ package fs import ( "bytes" "io" - "io/ioutil" "os" "gotest.tools/v3/assert" @@ -78,8 +77,8 @@ func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error { return applyPathOps(exp, ops) } -// Expected returns a Manifest with a directory structured created by ops. The -// PathOp operations are applied to the manifest as expectations of the +// Expected returns a [Manifest] with a directory structured created by ops. The +// [PathOp] operations are applied to the manifest as expectations of the // filesystem structure and properties. func Expected(t assert.TestingT, ops ...PathOp) Manifest { if ht, ok := t.(helperT); ok { @@ -124,9 +123,9 @@ func normalizeID(id int) uint32 { return uint32(id) } -var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil)) +var anyFileContent = io.NopCloser(bytes.NewReader(nil)) -// MatchAnyFileContent is a PathOp that updates a Manifest so that the file +// MatchAnyFileContent is a [PathOp] that updates a [Manifest] so that the file // at path may contain any content. func MatchAnyFileContent(path Path) error { if m, ok := path.(*filePath); ok { @@ -135,7 +134,7 @@ func MatchAnyFileContent(path Path) error { return nil } -// MatchContentIgnoreCarriageReturn is a PathOp that ignores cariage return +// MatchContentIgnoreCarriageReturn is a [PathOp] that ignores cariage return // discrepancies. func MatchContentIgnoreCarriageReturn(path Path) error { if m, ok := path.(*filePath); ok { @@ -146,7 +145,7 @@ func MatchContentIgnoreCarriageReturn(path Path) error { const anyFile = "*" -// MatchExtraFiles is a PathOp that updates a Manifest to allow a directory +// MatchExtraFiles is a [PathOp] that updates a [Manifest] to allow a directory // to contain unspecified files. func MatchExtraFiles(path Path) error { if m, ok := path.(*directoryPath); ok { @@ -157,14 +156,14 @@ func MatchExtraFiles(path Path) error { // CompareResult is the result of comparison. // -// See gotest.tools/assert/cmp.StringResult for a convenient implementation of +// See [gotest.tools/v3/assert/cmp.StringResult] for a convenient implementation of // this interface. type CompareResult interface { Success() bool FailureMessage() string } -// MatchFileContent is a PathOp that updates a Manifest to use the provided +// MatchFileContent is a [PathOp] that updates a [Manifest] to use the provided // function to determine if a file's content matches the expectation. func MatchFileContent(f func([]byte) CompareResult) PathOp { return func(path Path) error { @@ -175,7 +174,7 @@ func MatchFileContent(f func([]byte) CompareResult) PathOp { } } -// MatchFilesWithGlob is a PathOp that updates a Manifest to match files using +// MatchFilesWithGlob is a [PathOp] that updates a [Manifest] to match files using // glob pattern, and check them using the ops. func MatchFilesWithGlob(glob string, ops ...PathOp) PathOp { return func(path Path) error { @@ -189,7 +188,7 @@ func MatchFilesWithGlob(glob string, ops ...PathOp) PathOp { // anyFileMode is represented by uint32_max const anyFileMode os.FileMode = 4294967295 -// MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path +// MatchAnyFileMode is a [PathOp] that updates a [Manifest] so that the resource at path // will match any file mode. func MatchAnyFileMode(path Path) error { if m, ok := path.(manifestResource); ok { diff --git a/vendor/gotest.tools/v3/fs/report.go b/vendor/gotest.tools/v3/fs/report.go index 0b2d73d97..952aa26ad 100644 --- a/vendor/gotest.tools/v3/fs/report.go +++ b/vendor/gotest.tools/v3/fs/report.go @@ -3,7 +3,7 @@ package fs import ( "bytes" "fmt" - "io/ioutil" + "io" "os" "path/filepath" "runtime" @@ -17,9 +17,9 @@ import ( // Equal compares a directory to the expected structured described by a manifest // and returns success if they match. If they do not match the failure message // will contain all the differences between the directory structure and the -// expected structure defined by the Manifest. +// expected structure defined by the [Manifest]. // -// Equal is a cmp.Comparison which can be used with assert.Assert(). +// Equal is a [cmp.Comparison] which can be used with [gotest.tools/v3/assert.Assert]. func Equal(path string, expected Manifest) cmp.Comparison { return func() cmp.Result { actual, err := manifestFromDir(path) @@ -72,7 +72,6 @@ func removeCarriageReturn(in []byte) []byte { return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) } -// nolint: gocyclo func eqFile(x, y *file) []problem { p := eqResource(x.resource, y.resource) @@ -87,9 +86,9 @@ func eqFile(x, y *file) []problem { return p } - xContent, xErr := ioutil.ReadAll(x.content) + xContent, xErr := io.ReadAll(x.content) defer x.content.Close() - yContent, yErr := ioutil.ReadAll(y.content) + yContent, yErr := io.ReadAll(y.content) defer y.content.Close() if xErr != nil { @@ -159,7 +158,7 @@ func eqSymlink(x, y *symlink) []problem { func eqDirectory(path string, x, y *directory) []failure { p := eqResource(x.resource, y.resource) - var f []failure // nolint: prealloc + var f []failure matchedFiles := make(map[string]bool) for _, name := range sortedKeys(x.items) { diff --git a/vendor/gotest.tools/v3/internal/assert/assert.go b/vendor/gotest.tools/v3/internal/assert/assert.go index 8dc01f4b5..2dd80255a 100644 --- a/vendor/gotest.tools/v3/internal/assert/assert.go +++ b/vendor/gotest.tools/v3/internal/assert/assert.go @@ -1,3 +1,4 @@ +// Package assert provides internal utilties for assertions. package assert import ( @@ -23,7 +24,6 @@ type helperT interface { const failureMessage = "assertion failed: " // Eval the comparison and print a failure messages if the comparison has failed. -// nolint: gocyclo func Eval( t LogT, argSelector argSelector, @@ -115,7 +115,7 @@ func failureMsgFromError(err error) string { } func boolFailureMessage(expr ast.Expr) (string, error) { - if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ { + if binaryExpr, ok := expr.(*ast.BinaryExpr); ok { x, err := source.FormatNode(binaryExpr.X) if err != nil { return "", err @@ -124,7 +124,21 @@ func boolFailureMessage(expr ast.Expr) (string, error) { if err != nil { return "", err } - return x + " is " + y, nil + + switch binaryExpr.Op { + case token.NEQ: + return x + " is " + y, nil + case token.EQL: + return x + " is not " + y, nil + case token.GTR: + return x + " is <= " + y, nil + case token.LSS: + return x + " is >= " + y, nil + case token.GEQ: + return x + " is less than " + y, nil + case token.LEQ: + return x + " is greater than " + y, nil + } } if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT { @@ -135,6 +149,10 @@ func boolFailureMessage(expr ast.Expr) (string, error) { return x + " is true", nil } + if ident, ok := expr.(*ast.Ident); ok { + return ident.Name + " is false", nil + } + formatted, err := source.FormatNode(expr) if err != nil { return "", err diff --git a/vendor/gotest.tools/v3/internal/assert/result.go b/vendor/gotest.tools/v3/internal/assert/result.go index 20cd54129..bb8741eb4 100644 --- a/vendor/gotest.tools/v3/internal/assert/result.go +++ b/vendor/gotest.tools/v3/internal/assert/result.go @@ -1,6 +1,7 @@ package assert import ( + "errors" "fmt" "go/ast" @@ -25,6 +26,22 @@ func RunComparison( return true } + if source.IsUpdate() { + if updater, ok := result.(updateExpected); ok { + const stackIndex = 3 // Assert/Check, assert, RunComparison + err := updater.UpdatedExpected(stackIndex) + switch { + case err == nil: + return true + case errors.Is(err, source.ErrNotFound): + // do nothing, fallthrough to regular failure message + default: + t.Log("failed to update source", err) + return false + } + } + } + var message string switch typed := result.(type) { case resultWithComparisonArgs: @@ -52,6 +69,10 @@ type resultBasic interface { FailureMessage() string } +type updateExpected interface { + UpdatedExpected(stackIndex int) error +} + // filterPrintableExpr filters the ast.Expr slice to only include Expr that are // easy to read when printed and contain relevant information to an assertion. // diff --git a/vendor/gotest.tools/v3/internal/cleanup/cleanup.go b/vendor/gotest.tools/v3/internal/cleanup/cleanup.go index 58206e57f..6e7d3a3b7 100644 --- a/vendor/gotest.tools/v3/internal/cleanup/cleanup.go +++ b/vendor/gotest.tools/v3/internal/cleanup/cleanup.go @@ -1,4 +1,5 @@ -/*Package cleanup handles migration to and support for the Go 1.14+ +/* +Package cleanup handles migration to and support for the Go 1.14+ testing.TB.Cleanup() function. */ package cleanup diff --git a/vendor/gotest.tools/v3/internal/format/diff.go b/vendor/gotest.tools/v3/internal/format/diff.go index 9897d4b9d..4f6c07a35 100644 --- a/vendor/gotest.tools/v3/internal/format/diff.go +++ b/vendor/gotest.tools/v3/internal/format/diff.go @@ -1,3 +1,4 @@ +// Package format provides utilities for formatting diffs and messages. package format import ( diff --git a/vendor/gotest.tools/v3/internal/source/defers.go b/vendor/gotest.tools/v3/internal/source/defers.go index 66cfafbb6..392d9fe07 100644 --- a/vendor/gotest.tools/v3/internal/source/defers.go +++ b/vendor/gotest.tools/v3/internal/source/defers.go @@ -1,10 +1,9 @@ package source import ( + "fmt" "go/ast" "go/token" - - "github.com/pkg/errors" ) func scanToDeferLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { @@ -29,11 +28,11 @@ func guessDefer(node ast.Node) (ast.Node, error) { defers := collectDefers(node) switch len(defers) { case 0: - return nil, errors.New("failed to expression in defer") + return nil, fmt.Errorf("failed to find expression in defer") case 1: return defers[0].Call, nil default: - return nil, errors.Errorf( + return nil, fmt.Errorf( "ambiguous call expression: multiple (%d) defers in call block", len(defers)) } diff --git a/vendor/gotest.tools/v3/internal/source/source.go b/vendor/gotest.tools/v3/internal/source/source.go index c2eef0337..a4fc24ee6 100644 --- a/vendor/gotest.tools/v3/internal/source/source.go +++ b/vendor/gotest.tools/v3/internal/source/source.go @@ -1,7 +1,9 @@ +// Package source provides utilities for handling source-code. package source // import "gotest.tools/v3/internal/source" import ( "bytes" + "errors" "fmt" "go/ast" "go/format" @@ -9,14 +11,8 @@ import ( "go/token" "os" "runtime" - "strconv" - "strings" - - "github.com/pkg/errors" ) -const baseStackIndex = 1 - // FormattedCallExprArg returns the argument from an ast.CallExpr at the // index in the call stack. The argument is formatted using FormatNode. func FormattedCallExprArg(stackIndex int, argPos int) (string, error) { @@ -33,28 +29,26 @@ func FormattedCallExprArg(stackIndex int, argPos int) (string, error) { // CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at // the index in the call stack. func CallExprArgs(stackIndex int) ([]ast.Expr, error) { - _, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex) + _, filename, line, ok := runtime.Caller(stackIndex + 1) if !ok { return nil, errors.New("failed to get call stack") } - debug("call stack position: %s:%d", filename, lineNum) + debug("call stack position: %s:%d", filename, line) - node, err := getNodeAtLine(filename, lineNum) + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to parse source file %s: %w", filename, err) } - debug("found node: %s", debugFormatNode{node}) - return getCallExprArgs(node) -} - -func getNodeAtLine(filename string, lineNum int) (ast.Node, error) { - fileset := token.NewFileSet() - astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) + expr, err := getCallExprArgs(fileset, astFile, line) if err != nil { - return nil, errors.Wrapf(err, "failed to parse source file: %s", filename) + return nil, fmt.Errorf("call from %s:%d: %w", filename, line, err) } + return expr, nil +} +func getNodeAtLine(fileset *token.FileSet, astFile ast.Node, lineNum int) (ast.Node, error) { if node := scanToLine(fileset, astFile, lineNum); node != nil { return node, nil } @@ -64,8 +58,7 @@ func getNodeAtLine(filename string, lineNum int) (ast.Node, error) { return node, err } } - return nil, errors.Errorf( - "failed to find an expression on line %d in %s", lineNum, filename) + return nil, nil } func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { @@ -74,7 +67,7 @@ func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { switch { case node == nil || matchedNode != nil: return false - case nodePosition(fileset, node).Line == lineNum: + case fileset.Position(node.Pos()).Line == lineNum: matchedNode = node return false } @@ -83,46 +76,17 @@ func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { return matchedNode } -// In golang 1.9 the line number changed from being the line where the statement -// ended to the line where the statement began. -func nodePosition(fileset *token.FileSet, node ast.Node) token.Position { - if goVersionBefore19 { - return fileset.Position(node.End()) - } - return fileset.Position(node.Pos()) -} - -// GoVersionLessThan returns true if runtime.Version() is semantically less than -// version major.minor. Returns false if a release version can not be parsed from -// runtime.Version(). -func GoVersionLessThan(major, minor int64) bool { - version := runtime.Version() - // not a release version - if !strings.HasPrefix(version, "go") { - return false - } - version = strings.TrimPrefix(version, "go") - parts := strings.Split(version, ".") - if len(parts) < 2 { - return false - } - rMajor, err := strconv.ParseInt(parts[0], 10, 32) - if err != nil { - return false - } - if rMajor != major { - return rMajor < major - } - rMinor, err := strconv.ParseInt(parts[1], 10, 32) - if err != nil { - return false +func getCallExprArgs(fileset *token.FileSet, astFile ast.Node, line int) ([]ast.Expr, error) { + node, err := getNodeAtLine(fileset, astFile, line) + switch { + case err != nil: + return nil, err + case node == nil: + return nil, fmt.Errorf("failed to find an expression") } - return rMinor < minor -} -var goVersionBefore19 = GoVersionLessThan(1, 9) + debug("found node: %s", debugFormatNode{node}) -func getCallExprArgs(node ast.Node) ([]ast.Expr, error) { visitor := &callExprVisitor{} ast.Walk(visitor, node) if visitor.expr == nil { @@ -173,6 +137,9 @@ type debugFormatNode struct { } func (n debugFormatNode) String() string { + if n.Node == nil { + return "none" + } out, err := FormatNode(n.Node) if err != nil { return fmt.Sprintf("failed to format %s: %s", n.Node, err) diff --git a/vendor/gotest.tools/v3/internal/source/update.go b/vendor/gotest.tools/v3/internal/source/update.go new file mode 100644 index 000000000..5591bffd1 --- /dev/null +++ b/vendor/gotest.tools/v3/internal/source/update.go @@ -0,0 +1,171 @@ +package source + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" + "runtime" + "strings" +) + +// IsUpdate is returns true if the -update flag is set. It indicates the user +// running the tests would like to update any golden values. +func IsUpdate() bool { + if Update { + return true + } + return flag.Lookup("update").Value.(flag.Getter).Get().(bool) +} + +// Update is a shim for testing, and for compatibility with the old -update-golden +// flag. +var Update bool + +func init() { + if f := flag.Lookup("update"); f != nil { + getter, ok := f.Value.(flag.Getter) + msg := "some other package defined an incompatible -update flag, expected a flag.Bool" + if !ok { + panic(msg) + } + if _, ok := getter.Get().(bool); !ok { + panic(msg) + } + return + } + flag.Bool("update", false, "update golden values") +} + +// ErrNotFound indicates that UpdateExpectedValue failed to find the +// variable to update, likely because it is not a package level variable. +var ErrNotFound = fmt.Errorf("failed to find variable for update of golden value") + +// UpdateExpectedValue looks for a package-level variable with a name that +// starts with expected in the arguments to the caller. If the variable is +// found, the value of the variable will be updated to value of the other +// argument to the caller. +func UpdateExpectedValue(stackIndex int, x, y interface{}) error { + _, filename, line, ok := runtime.Caller(stackIndex + 1) + if !ok { + return errors.New("failed to get call stack") + } + debug("call stack position: %s:%d", filename, line) + + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors|parser.ParseComments) + if err != nil { + return fmt.Errorf("failed to parse source file %s: %w", filename, err) + } + + expr, err := getCallExprArgs(fileset, astFile, line) + if err != nil { + return fmt.Errorf("call from %s:%d: %w", filename, line, err) + } + + if len(expr) < 3 { + debug("not enough arguments %d: %v", + len(expr), debugFormatNode{Node: &ast.CallExpr{Args: expr}}) + return ErrNotFound + } + + argIndex, ident := getIdentForExpectedValueArg(expr) + if argIndex < 0 || ident == nil { + debug("no arguments started with the word 'expected': %v", + debugFormatNode{Node: &ast.CallExpr{Args: expr}}) + return ErrNotFound + } + + value := x + if argIndex == 1 { + value = y + } + + strValue, ok := value.(string) + if !ok { + debug("value must be type string, got %T", value) + return ErrNotFound + } + return UpdateVariable(filename, fileset, astFile, ident, strValue) +} + +// UpdateVariable writes to filename the contents of astFile with the value of +// the variable updated to value. +func UpdateVariable( + filename string, + fileset *token.FileSet, + astFile *ast.File, + ident *ast.Ident, + value string, +) error { + obj := ident.Obj + if obj == nil { + return ErrNotFound + } + if obj.Kind != ast.Con && obj.Kind != ast.Var { + debug("can only update var and const, found %v", obj.Kind) + return ErrNotFound + } + + switch decl := obj.Decl.(type) { + case *ast.ValueSpec: + if len(decl.Names) != 1 { + debug("more than one name in ast.ValueSpec") + return ErrNotFound + } + + decl.Values[0] = &ast.BasicLit{ + Kind: token.STRING, + Value: "`" + value + "`", + } + + case *ast.AssignStmt: + if len(decl.Lhs) != 1 { + debug("more than one name in ast.AssignStmt") + return ErrNotFound + } + + decl.Rhs[0] = &ast.BasicLit{ + Kind: token.STRING, + Value: "`" + value + "`", + } + + default: + debug("can only update *ast.ValueSpec, found %T", obj.Decl) + return ErrNotFound + } + + var buf bytes.Buffer + if err := format.Node(&buf, fileset, astFile); err != nil { + return fmt.Errorf("failed to format file after update: %w", err) + } + + fh, err := os.Create(filename) + if err != nil { + return fmt.Errorf("failed to open file %v: %w", filename, err) + } + if _, err = fh.Write(buf.Bytes()); err != nil { + return fmt.Errorf("failed to write file %v: %w", filename, err) + } + if err := fh.Sync(); err != nil { + return fmt.Errorf("failed to sync file %v: %w", filename, err) + } + return nil +} + +func getIdentForExpectedValueArg(expr []ast.Expr) (int, *ast.Ident) { + for i := 1; i < 3; i++ { + switch e := expr[i].(type) { + case *ast.Ident: + if strings.HasPrefix(strings.ToLower(e.Name), "expected") { + return i, e + } + } + } + return -1, nil +} diff --git a/vendor/gotest.tools/v3/internal/source/version.go b/vendor/gotest.tools/v3/internal/source/version.go new file mode 100644 index 000000000..5fa8a9031 --- /dev/null +++ b/vendor/gotest.tools/v3/internal/source/version.go @@ -0,0 +1,35 @@ +package source + +import ( + "runtime" + "strconv" + "strings" +) + +// GoVersionLessThan returns true if runtime.Version() is semantically less than +// version major.minor. Returns false if a release version can not be parsed from +// runtime.Version(). +func GoVersionLessThan(major, minor int64) bool { + version := runtime.Version() + // not a release version + if !strings.HasPrefix(version, "go") { + return false + } + version = strings.TrimPrefix(version, "go") + parts := strings.Split(version, ".") + if len(parts) < 2 { + return false + } + rMajor, err := strconv.ParseInt(parts[0], 10, 32) + if err != nil { + return false + } + if rMajor != major { + return rMajor < major + } + rMinor, err := strconv.ParseInt(parts[1], 10, 32) + if err != nil { + return false + } + return rMinor < minor +} diff --git a/vendor/modules.txt b/vendor/modules.txt index f9048909d..629a1506b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -127,8 +127,8 @@ golang.org/x/sys/unix golang.org/x/sys/windows # gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 ## explicit -# gotest.tools/v3 v3.0.3 -## explicit; go 1.11 +# gotest.tools/v3 v3.5.1 +## explicit; go 1.17 gotest.tools/v3/assert gotest.tools/v3/assert/cmp gotest.tools/v3/env