From 28b0aa9f1a38d993f2d243ae761236db8208983e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 22 Sep 2022 16:31:28 +0200 Subject: [PATCH 1/2] replace uses of deprecated env.Patch() Also removing redundant defer for env.PatchAll(), which is now automatically handled in t.Cleanup() Signed-off-by: Sebastiaan van Stijn --- cli/command/cli_test.go | 5 ++--- cli/command/defaultcontextstore_test.go | 3 +-- cli/command/image/build_test.go | 13 ++++++------- cli/command/image/trust_test.go | 4 ++-- cli/compose/interpolation/interpolation_test.go | 6 ++---- cli/config/config_test.go | 2 +- cmd/docker/builder_test.go | 5 ++--- e2e/image/build_test.go | 9 ++++----- opts/parse_test.go | 3 +-- 9 files changed, 21 insertions(+), 29 deletions(-) diff --git a/cli/command/cli_test.go b/cli/command/cli_test.go index 73e17d7e4c..85a49aca17 100644 --- a/cli/command/cli_test.go +++ b/cli/command/cli_test.go @@ -23,7 +23,6 @@ import ( "github.com/docker/docker/client" "github.com/pkg/errors" "gotest.tools/v3/assert" - "gotest.tools/v3/env" "gotest.tools/v3/fs" ) @@ -89,8 +88,8 @@ func TestNewAPIClientFromFlagsWithCustomHeaders(t *testing.T) { func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) { customVersion := "v3.3.3" - defer env.Patch(t, "DOCKER_API_VERSION", customVersion)() - defer env.Patch(t, "DOCKER_HOST", ":2375")() + t.Setenv("DOCKER_API_VERSION", customVersion) + t.Setenv("DOCKER_HOST", ":2375") opts := &flags.CommonOptions{} configFile := &configfile.ConfigFile{} diff --git a/cli/command/defaultcontextstore_test.go b/cli/command/defaultcontextstore_test.go index cb45c60dd4..4b00b49f9e 100644 --- a/cli/command/defaultcontextstore_test.go +++ b/cli/command/defaultcontextstore_test.go @@ -10,7 +10,6 @@ import ( cliflags "github.com/docker/cli/cli/flags" "github.com/docker/go-connections/tlsconfig" "gotest.tools/v3/assert" - "gotest.tools/v3/env" "gotest.tools/v3/golden" ) @@ -53,7 +52,7 @@ func testStore(t *testing.T, meta store.Metadata, tls store.ContextTLSData) stor func TestDefaultContextInitializer(t *testing.T) { cli, err := NewDockerCli() assert.NilError(t, err) - defer env.Patch(t, "DOCKER_HOST", "ssh://someswarmserver")() + t.Setenv("DOCKER_HOST", "ssh://someswarmserver") cli.configFile = &configfile.ConfigFile{} ctx, err := ResolveDefaultContext(&cliflags.CommonOptions{ TLS: true, diff --git a/cli/command/image/build_test.go b/cli/command/image/build_test.go index 94ea7a4411..bb308869d2 100644 --- a/cli/command/image/build_test.go +++ b/cli/command/image/build_test.go @@ -17,13 +17,12 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/google/go-cmp/cmp" "gotest.tools/v3/assert" - "gotest.tools/v3/env" "gotest.tools/v3/fs" "gotest.tools/v3/skip" ) func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") buffer := new(bytes.Buffer) fakeBuild := newFakeBuild() fakeImageBuild := func(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { @@ -59,8 +58,8 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { } func TestRunBuildResetsUidAndGidInContext(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() skip.If(t, os.Getuid() != 0, "root is required to chown files") + t.Setenv("DOCKER_BUILDKIT", "0") fakeBuild := newFakeBuild() cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeBuild.build}) @@ -90,7 +89,7 @@ func TestRunBuildResetsUidAndGidInContext(t *testing.T) { } func TestRunBuildDockerfileOutsideContext(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dir := fs.NewDir(t, t.Name(), fs.WithFile("data", "data file")) defer dir.Remove() @@ -123,7 +122,7 @@ COPY data /data // TODO: test "context selection" logic directly when runBuild is refactored // to support testing (ex: docker/cli#294) func TestRunBuildFromGitHubSpecialCase(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") cmd := NewBuildCommand(test.NewFakeCli(&fakeClient{})) // Clone a small repo that exists so git doesn't prompt for credentials cmd.SetArgs([]string{"github.com/docker/for-win"}) @@ -137,7 +136,7 @@ func TestRunBuildFromGitHubSpecialCase(t *testing.T) { // starting with `github.com` takes precedence over the `github.com` special // case. func TestRunBuildFromLocalGitHubDir(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") buildDir := filepath.Join(t.TempDir(), "github.com", "docker", "no-such-repository") err := os.MkdirAll(buildDir, 0777) @@ -154,7 +153,7 @@ func TestRunBuildFromLocalGitHubDir(t *testing.T) { } func TestRunBuildWithSymlinkedContext(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dockerfile := ` FROM alpine:3.6 RUN echo hello world diff --git a/cli/command/image/trust_test.go b/cli/command/image/trust_test.go index 3fcf77c52c..eba38cbb97 100644 --- a/cli/command/image/trust_test.go +++ b/cli/command/image/trust_test.go @@ -13,7 +13,7 @@ import ( ) func TestENVTrustServer(t *testing.T) { - defer env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "https://notary-test.example.com:5000"})() + env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "https://notary-test.example.com:5000"}) indexInfo := ®istrytypes.IndexInfo{Name: "testserver"} output, err := trust.Server(indexInfo) expectedStr := "https://notary-test.example.com:5000" @@ -23,7 +23,7 @@ func TestENVTrustServer(t *testing.T) { } func TestHTTPENVTrustServer(t *testing.T) { - defer env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "http://notary-test.example.com:5000"})() + env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "http://notary-test.example.com:5000"}) indexInfo := ®istrytypes.IndexInfo{Name: "testserver"} _, err := trust.Server(indexInfo) if err == nil { diff --git a/cli/compose/interpolation/interpolation_test.go b/cli/compose/interpolation/interpolation_test.go index 536da0013b..0e70d1bbbd 100644 --- a/cli/compose/interpolation/interpolation_test.go +++ b/cli/compose/interpolation/interpolation_test.go @@ -1,13 +1,11 @@ package interpolation import ( - "testing" - "strconv" + "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/env" ) var defaults = map[string]string{ @@ -62,7 +60,7 @@ func TestInvalidInterpolation(t *testing.T) { } func TestInterpolateWithDefaults(t *testing.T) { - defer env.Patch(t, "FOO", "BARZ")() + t.Setenv("FOO", "BARZ") config := map[string]interface{}{ "networks": map[string]interface{}{ diff --git a/cli/config/config_test.go b/cli/config/config_test.go index 9af2490870..1f95f25306 100644 --- a/cli/config/config_test.go +++ b/cli/config/config_test.go @@ -97,7 +97,7 @@ func TestOldJSONFallbackDeprecationWarning(t *testing.T) { js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` tmpHome := fs.NewDir(t, t.Name(), fs.WithFile(oldConfigfile, js)) defer tmpHome.Remove() - defer env.PatchAll(t, map[string]string{homeKey: tmpHome.Path(), "DOCKER_CONFIG": ""})() + env.PatchAll(t, map[string]string{homeKey: tmpHome.Path(), "DOCKER_CONFIG": ""}) // reset the homeDir, configDir, and its sync.Once, to force them being resolved again resetHomeDir() diff --git a/cmd/docker/builder_test.go b/cmd/docker/builder_test.go index c7e70d61f4..b4ade48912 100644 --- a/cmd/docker/builder_test.go +++ b/cmd/docker/builder_test.go @@ -9,7 +9,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/internal/test/output" "gotest.tools/v3/assert" - "gotest.tools/v3/env" "gotest.tools/v3/fs" ) @@ -45,7 +44,7 @@ echo '{"SchemaVersion":"0.1.0","Vendor":"Docker Inc.","Version":"v0.6.3","ShortD } func TestBuildkitDisabled(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dir := fs.NewDir(t, t.Name(), fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0777)), @@ -102,7 +101,7 @@ func TestBuilderBroken(t *testing.T) { } func TestBuilderBrokenEnforced(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "1")() + t.Setenv("DOCKER_BUILDKIT", "1") dir := fs.NewDir(t, t.Name(), fs.WithFile(pluginFilename, `#!/bin/sh exit 1`, fs.WithMode(0777)), diff --git a/e2e/image/build_test.go b/e2e/image/build_test.go index db298e40e7..00e79e5c52 100644 --- a/e2e/image/build_test.go +++ b/e2e/image/build_test.go @@ -12,14 +12,13 @@ import ( "github.com/docker/cli/internal/test/output" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/env" "gotest.tools/v3/fs" "gotest.tools/v3/icmd" "gotest.tools/v3/skip" ) func TestBuildFromContextDirectoryWithTag(t *testing.T) { - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dir := fs.NewDir(t, "test-build-context-dir", fs.WithFile("run", "echo running", fs.WithMode(0755)), @@ -58,7 +57,7 @@ func TestBuildFromContextDirectoryWithTag(t *testing.T) { func TestTrustedBuild(t *testing.T) { skip.If(t, environment.RemoteDaemon()) - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dir := fixtures.SetupConfigFile(t) defer dir.Remove() @@ -93,7 +92,7 @@ func TestTrustedBuild(t *testing.T) { func TestTrustedBuildUntrustedImage(t *testing.T) { skip.If(t, environment.RemoteDaemon()) - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dir := fixtures.SetupConfigFile(t) defer dir.Remove() @@ -120,7 +119,7 @@ func TestTrustedBuildUntrustedImage(t *testing.T) { func TestBuildIidFileSquash(t *testing.T) { environment.SkipIfNotExperimentalDaemon(t) - defer env.Patch(t, "DOCKER_BUILDKIT", "0")() + t.Setenv("DOCKER_BUILDKIT", "0") dir := fs.NewDir(t, "test-iidfile-squash") defer dir.Remove() diff --git a/opts/parse_test.go b/opts/parse_test.go index c6152f815b..f7ab13a299 100644 --- a/opts/parse_test.go +++ b/opts/parse_test.go @@ -4,7 +4,6 @@ import ( "testing" "gotest.tools/v3/assert" - "gotest.tools/v3/env" "gotest.tools/v3/fs" ) @@ -23,7 +22,7 @@ NO_SUCH_ENV defer envFile1.Remove() envFile2 := fs.NewFile(t, t.Name(), fs.WithContent("Z2=z\nA2=a")) defer envFile2.Remove() - defer env.Patch(t, "FROM_ENV", "from-env")() + t.Setenv("FROM_ENV", "from-env") tests := []struct { name string From ef2a8266362c2d8fb0b5b8db4676b01c111eed1d Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 22 Sep 2022 15:38:19 +0200 Subject: [PATCH 2/2] vendor: gotest.tools v3.3.0 full diff: https://github.com/gotestyourself/gotest.tools/compare/v3.1.0...v3.3.0 - golden: accept -update for updating files - assert: golden variables Signed-off-by: Sebastiaan van Stijn --- vendor.mod | 2 +- vendor.sum | 4 +- vendor/gotest.tools/v3/assert/assert.go | 233 ++++++++++++------ vendor/gotest.tools/v3/assert/assert_go113.go | 20 -- vendor/gotest.tools/v3/assert/cmp/compare.go | 41 ++- .../v3/assert/cmp/compare_go113.go | 29 --- vendor/gotest.tools/v3/assert/cmp/result.go | 11 + vendor/gotest.tools/v3/env/env.go | 2 + vendor/gotest.tools/v3/fs/manifest.go | 4 +- vendor/gotest.tools/v3/fs/ops.go | 8 +- vendor/gotest.tools/v3/fs/report.go | 3 +- vendor/gotest.tools/v3/golden/golden.go | 25 +- vendor/gotest.tools/v3/icmd/command.go | 1 - vendor/gotest.tools/v3/icmd/exitcode.go | 32 +-- .../gotest.tools/v3/internal/assert/assert.go | 23 +- .../gotest.tools/v3/internal/assert/result.go | 21 ++ .../gotest.tools/v3/internal/source/defers.go | 7 +- .../gotest.tools/v3/internal/source/source.go | 84 ++----- .../gotest.tools/v3/internal/source/update.go | 138 +++++++++++ .../v3/internal/source/version.go | 35 +++ vendor/modules.txt | 4 +- 21 files changed, 492 insertions(+), 235 deletions(-) delete mode 100644 vendor/gotest.tools/v3/assert/assert_go113.go delete mode 100644 vendor/gotest.tools/v3/assert/cmp/compare_go113.go create mode 100644 vendor/gotest.tools/v3/internal/source/update.go create mode 100644 vendor/gotest.tools/v3/internal/source/version.go diff --git a/vendor.mod b/vendor.mod index b0df2284cf..4215cd1ec1 100644 --- a/vendor.mod +++ b/vendor.mod @@ -40,7 +40,7 @@ require ( golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 golang.org/x/text v0.3.7 gopkg.in/yaml.v2 v2.4.0 - gotest.tools/v3 v3.1.0 + gotest.tools/v3 v3.3.0 ) require ( diff --git a/vendor.sum b/vendor.sum index 09dfdee66d..ba242f1941 100644 --- a/vendor.sum +++ b/vendor.sum @@ -749,8 +749,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/gotest.tools/v3/assert/assert.go b/vendor/gotest.tools/v3/assert/assert.go index f3f805dde9..dbd4f5a016 100644 --- a/vendor/gotest.tools/v3/assert/assert.go +++ b/vendor/gotest.tools/v3/assert/assert.go @@ -1,51 +1,80 @@ /*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. +values in tests. When an assertion fails a helpful error message is printed. Example usage -The example below shows assert used with some common types. +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. + assert.NilError(t, err) + // filename_test.go:212: assertion failed: error is not nil: file not found - import ( - "testing" +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. - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - ) +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. - func TestEverything(t *testing.T) { - // booleans - assert.Assert(t, ok) - assert.Assert(t, !missing) + // booleans - // primitives - assert.Equal(t, count, 1) - assert.Equal(t, msg, "the message") - assert.Assert(t, total != 10) // NotEqual + assert.Assert(t, ok) + // assertion failed: ok is false + assert.Assert(t, !missing) + // assertion failed: missing is true - // 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) + // primitives - // 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.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 - // pointers and interface - assert.Assert(t, is.Nil(ref)) - assert.Assert(t, ref != nil) // NotNil - } + // errors + + 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) + + // 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 + +Assert and Check + +Assert and Check are very similar, they both accept a 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 @@ -70,7 +99,8 @@ 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. @@ -88,16 +118,28 @@ 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. +// 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. +// 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. -// A non-nil error is a failure, err.Error() is used as the failure message. +// 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 t.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,8 +150,8 @@ 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. func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool { @@ -123,8 +165,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 t.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 +183,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 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() @@ -161,7 +214,10 @@ func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { // Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional // commonly used Options. // -// This is equivalent to Assert(t, cmp.DeepEqual(x, y)). +// DeepEqual uses 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 +227,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 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 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 +264,29 @@ 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)). +// Most new code should use ErrorIs instead. ErrorType may be deprecated in the +// future. // // 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. +// 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 -// Fails if err does not implement the reflect.Type +// The assertion fails if err does not implement the reflect.Type. +// +// ErrorType uses 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. func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -217,3 +295,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 https://golang.org/pkg/errors/#Is for +// accepted arguments. +// +// ErrorIs uses 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/assert_go113.go b/vendor/gotest.tools/v3/assert/assert_go113.go deleted file mode 100644 index 1e3cede48a..0000000000 --- a/vendor/gotest.tools/v3/assert/assert_go113.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build go1.13 - -package assert - -import ( - "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/internal/assert" -) - -// ErrorIs fails the test if err is nil, or the error does not match expected -// when compared using errors.Is. See https://golang.org/pkg/errors/#Is for -// accepted argument values. -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 3c0e05ab5f..78f76e4e88 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" @@ -34,7 +35,7 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { if diff == "" { return ResultSuccess } - return multiLineDiffResult(diff) + return multiLineDiffResult(diff, x, y) } } @@ -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. @@ -165,7 +166,7 @@ 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) } @@ -307,7 +309,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 +365,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 +// https://golang.org/pkg/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/compare_go113.go b/vendor/gotest.tools/v3/assert/cmp/compare_go113.go deleted file mode 100644 index 58af74450d..0000000000 --- a/vendor/gotest.tools/v3/assert/cmp/compare_go113.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build go1.13 - -package cmp - -import ( - "errors" -) - -// ErrorIs succeeds if errors.Is(actual, expected) returns true. See -// https://golang.org/pkg/errors/#Is for accepted argument values. -func ErrorIs(actual error, expected error) Comparison { - return func() Result { - if errors.Is(actual, expected) { - return ResultSuccess - } - - return ResultFailureTemplate(`error is - {{- if not .Data.a }} nil,{{ else }} - {{- printf " \"%v\"" .Data.a}} ( - {{- with callArg 0 }}{{ formatNode . }} {{end -}} - {{- printf "%T" .Data.a -}} - ), - {{- end }} not {{ printf "\"%v\"" .Data.x}} ( - {{- with callArg 1 }}{{ formatNode . }} {{end -}} - {{- printf "%T" .Data.x -}} - )`, - 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 3b48d9bf0c..28ef8d3d46 100644 --- a/vendor/gotest.tools/v3/assert/cmp/result.go +++ b/vendor/gotest.tools/v3/assert/cmp/result.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "go/ast" + "reflect" "text/template" "gotest.tools/v3/internal/source" @@ -68,6 +69,11 @@ func (r templatedResult) FailureMessage(args []ast.Expr) string { return msg } +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 @@ -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 7a71acd0d7..a06eab3ebe 100644 --- a/vendor/gotest.tools/v3/env/env.go +++ b/vendor/gotest.tools/v3/env/env.go @@ -21,6 +21,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() diff --git a/vendor/gotest.tools/v3/fs/manifest.go b/vendor/gotest.tools/v3/fs/manifest.go index d396756376..b657bd96a9 100644 --- a/vendor/gotest.tools/v3/fs/manifest.go +++ b/vendor/gotest.tools/v3/fs/manifest.go @@ -1,12 +1,12 @@ package fs import ( + "fmt" "io" "io/ioutil" "os" "path/filepath" - "github.com/pkg/errors" "gotest.tools/v3/assert" ) @@ -75,7 +75,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) diff --git a/vendor/gotest.tools/v3/fs/ops.go b/vendor/gotest.tools/v3/fs/ops.go index 4589623fa9..9e1e068b26 100644 --- a/vendor/gotest.tools/v3/fs/ops.go +++ b/vendor/gotest.tools/v3/fs/ops.go @@ -2,6 +2,7 @@ package fs import ( "bytes" + "fmt" "io" "io/ioutil" "os" @@ -9,7 +10,6 @@ import ( "strings" "time" - "github.com/pkg/errors" "gotest.tools/v3/assert" ) @@ -137,7 +137,7 @@ func WithFiles(files map[string]string) PathOp { 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()) } @@ -257,7 +257,7 @@ func WithSymlink(path, target string) PathOp { 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 +268,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/report.go b/vendor/gotest.tools/v3/fs/report.go index 0b2d73d977..1a3c6683bd 100644 --- a/vendor/gotest.tools/v3/fs/report.go +++ b/vendor/gotest.tools/v3/fs/report.go @@ -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) @@ -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/golden/golden.go b/vendor/gotest.tools/v3/golden/golden.go index 750e6c5dd5..47ea85fe02 100644 --- a/vendor/gotest.tools/v3/golden/golden.go +++ b/vendor/gotest.tools/v3/golden/golden.go @@ -2,7 +2,7 @@ Golden files are files in the ./testdata/ subdirectory of the package under test. Golden files can be automatically updated to match new values by running -`go test pkgname -test.update-golden`. To ensure the update is correct +`go test pkgname -update`. To ensure the update is correct compare the diff of the old expected value to the new expected value. */ package golden // import "gotest.tools/v3/golden" @@ -18,9 +18,12 @@ import ( "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" "gotest.tools/v3/internal/format" + "gotest.tools/v3/internal/source" ) -var flagUpdate = flag.Bool("test.update-golden", false, "update golden file") +func init() { + flag.BoolVar(&source.Update, "test.update-golden", false, "deprecated flag") +} type helperT interface { Helper() @@ -28,7 +31,7 @@ type helperT interface { // NormalizeCRLFToLF enables end-of-line normalization for actual values passed // to Assert and String, as well as the values saved to golden files with -// -test.update-golden. +// -update. // // Defaults to true. If you use the core.autocrlf=true git setting on windows // you will need to set this to false. @@ -39,9 +42,9 @@ type helperT interface { // The default value may change in a future major release. var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "false" -// FlagUpdate returns true when the -test.update-golden flag has been set. +// FlagUpdate returns true when the -update flag has been set. func FlagUpdate() bool { - return *flagUpdate + return source.Update } // Open opens the file in ./testdata @@ -81,7 +84,7 @@ func removeCarriageReturn(in []byte) []byte { // Assert compares actual to the expected value in the golden file. // -// Running `go test pkgname -test.update-golden` will write the value of actual +// Running `go test pkgname -update` will write the value of actual // to the golden file. // // This is equivalent to assert.Assert(t, String(actual, filename)) @@ -95,7 +98,7 @@ func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...int // String compares actual to the contents of filename and returns success // if the strings are equal. // -// Running `go test pkgname -test.update-golden` will write the value of actual +// Running `go test pkgname -update` will write the value of actual // to the golden file. // // Any \r\n substrings in actual are converted to a single \n character @@ -122,13 +125,13 @@ func String(actual string, filename string) cmp.Comparison { func failurePostamble(filename string) string { return fmt.Sprintf(` -You can run 'go test . -test.update-golden' to automatically update %s to the new expected value.' +You can run 'go test . -update' to automatically update %s to the new expected value.' `, Path(filename)) } // AssertBytes compares actual to the expected value in the golden. // -// Running `go test pkgname -test.update-golden` will write the value of actual +// Running `go test pkgname -update` will write the value of actual // to the golden file. // // This is equivalent to assert.Assert(t, Bytes(actual, filename)) @@ -147,7 +150,7 @@ func AssertBytes( // Bytes compares actual to the contents of filename and returns success // if the bytes are equal. // -// Running `go test pkgname -test.update-golden` will write the value of actual +// Running `go test pkgname -update` will write the value of actual // to the golden file. func Bytes(actual []byte, filename string) cmp.Comparison { return func() cmp.Result { @@ -175,7 +178,7 @@ func compare(actual []byte, filename string) (cmp.Result, []byte) { } func update(filename string, actual []byte) error { - if !*flagUpdate { + if !source.Update { return nil } if dir := filepath.Dir(Path(filename)); dir != "." { diff --git a/vendor/gotest.tools/v3/icmd/command.go b/vendor/gotest.tools/v3/icmd/command.go index 822ee94bf0..9613322806 100644 --- a/vendor/gotest.tools/v3/icmd/command.go +++ b/vendor/gotest.tools/v3/icmd/command.go @@ -77,7 +77,6 @@ func (r *Result) Compare(exp Expected) error { return r.match(exp) } -// nolint: gocyclo func (r *Result) match(exp Expected) error { errors := []string{} add := func(format string, args ...interface{}) { diff --git a/vendor/gotest.tools/v3/icmd/exitcode.go b/vendor/gotest.tools/v3/icmd/exitcode.go index 751254a0e5..2e98f86c1a 100644 --- a/vendor/gotest.tools/v3/icmd/exitcode.go +++ b/vendor/gotest.tools/v3/icmd/exitcode.go @@ -1,32 +1,24 @@ package icmd import ( - "syscall" + "errors" - "github.com/pkg/errors" exec "golang.org/x/sys/execabs" ) -// getExitCode returns the ExitStatus of a process from the error returned by -// exec.Run(). If the exit status could not be parsed an error is returned. -func getExitCode(err error) (int, error) { - if exiterr, ok := err.(*exec.ExitError); ok { - if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { - return procExit.ExitStatus(), nil - } - } - return 0, errors.Wrap(err, "failed to get exit code") -} - -func processExitCode(err error) (exitCode int) { +func processExitCode(err error) int { if err == nil { return 0 } - exitCode, exiterr := getExitCode(err) - if exiterr != nil { - // TODO: Fix this so we check the error's text. - // we've failed to retrieve exit code, so we set it to 127 - return 127 + + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + if exitErr.ProcessState == nil { + return 0 + } + if code := exitErr.ProcessState.ExitCode(); code != -1 { + return code + } } - return exitCode + return 127 } diff --git a/vendor/gotest.tools/v3/internal/assert/assert.go b/vendor/gotest.tools/v3/internal/assert/assert.go index 8dc01f4b5e..0d67751da8 100644 --- a/vendor/gotest.tools/v3/internal/assert/assert.go +++ b/vendor/gotest.tools/v3/internal/assert/assert.go @@ -23,7 +23,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 +114,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 +123,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 +148,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 20cd54129e..3603206146 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.Update { + 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/source/defers.go b/vendor/gotest.tools/v3/internal/source/defers.go index 66cfafbb64..392d9fe071 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 c2eef03373..a3f70086d7 100644 --- a/vendor/gotest.tools/v3/internal/source/source.go +++ b/vendor/gotest.tools/v3/internal/source/source.go @@ -2,6 +2,7 @@ package source // import "gotest.tools/v3/internal/source" import ( "bytes" + "errors" "fmt" "go/ast" "go/format" @@ -9,14 +10,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 +28,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) - if err != nil { - return nil, 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) if err != nil { - return nil, errors.Wrapf(err, "failed to parse source file: %s", filename) + return nil, fmt.Errorf("failed to parse source file %s: %w", filename, err) } + expr, err := getCallExprArgs(fileset, astFile, line) + if err != nil { + 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 +57,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 +66,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 +75,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()) +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 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 - } - return rMinor < minor -} + debug("found node: %s", debugFormatNode{node}) -var goVersionBefore19 = GoVersionLessThan(1, 9) - -func getCallExprArgs(node ast.Node) ([]ast.Expr, error) { visitor := &callExprVisitor{} ast.Walk(visitor, node) if visitor.expr == nil { @@ -173,6 +136,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 0000000000..bd9678b831 --- /dev/null +++ b/vendor/gotest.tools/v3/internal/source/update.go @@ -0,0 +1,138 @@ +package source + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" + "runtime" + "strings" +) + +// Update is set by the -update flag. It indicates the user running the tests +// would like to update any golden values. +var Update bool + +func init() { + flag.BoolVar(&Update, "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, varName := getVarNameForExpectedValueArg(expr) + if argIndex < 0 || varName == "" { + 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, varName, 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, + varName string, + value string, +) error { + obj := astFile.Scope.Objects[varName] + 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 + } + + spec, ok := obj.Decl.(*ast.ValueSpec) + if !ok { + debug("can only update *ast.ValueSpec, found %T", obj.Decl) + return ErrNotFound + } + if len(spec.Names) != 1 { + debug("more than one name in ast.ValueSpec") + return ErrNotFound + } + + spec.Values[0] = &ast.BasicLit{ + Kind: token.STRING, + Value: "`" + value + "`", + } + + 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 getVarNameForExpectedValueArg(expr []ast.Expr) (int, string) { + 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.Name + } + } + } + return -1, "" +} 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 0000000000..5fa8a90312 --- /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 a102d54eac..2ebad07878 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -379,8 +379,8 @@ google.golang.org/protobuf/types/known/timestamppb # gopkg.in/yaml.v2 v2.4.0 ## explicit; go 1.15 gopkg.in/yaml.v2 -# gotest.tools/v3 v3.1.0 -## explicit; go 1.11 +# gotest.tools/v3 v3.3.0 +## explicit; go 1.13 gotest.tools/v3/assert gotest.tools/v3/assert/cmp gotest.tools/v3/env