瀏覽代碼

vendor: gotest.tools v3.3.0

full diff: https://github.com/gotestyourself/gotest.tools/compare/v3.2.0...v3.3.0

- golden: accept -update for updating files
- assert: golden variables

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 2 年之前
父節點
當前提交
3e1601a980

+ 1 - 1
vendor.mod

@@ -85,7 +85,7 @@ require (
 	golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
 	golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
 	google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
 	google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa
 	google.golang.org/grpc v1.45.0
 	google.golang.org/grpc v1.45.0
-	gotest.tools/v3 v3.2.0
+	gotest.tools/v3 v3.3.0
 )
 )
 
 
 require (
 require (

+ 2 - 2
vendor.sum

@@ -1700,8 +1700,8 @@ gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
 gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
-gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I=
-gotest.tools/v3 v3.2.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
+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-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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

+ 4 - 4
vendor/gotest.tools/v3/assert/cmp/compare.go

@@ -35,7 +35,7 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
 		if diff == "" {
 		if diff == "" {
 			return ResultSuccess
 			return ResultSuccess
 		}
 		}
-		return multiLineDiffResult(diff)
+		return multiLineDiffResult(diff, x, y)
 	}
 	}
 }
 }
 
 
@@ -102,7 +102,7 @@ func Equal(x, y interface{}) Comparison {
 			return ResultSuccess
 			return ResultSuccess
 		case isMultiLineStringCompare(x, y):
 		case isMultiLineStringCompare(x, y):
 			diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
 			diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
-			return multiLineDiffResult(diff)
+			return multiLineDiffResult(diff, x, y)
 		}
 		}
 		return ResultFailureTemplate(`
 		return ResultFailureTemplate(`
 			{{- printf "%v" .Data.x}} (
 			{{- printf "%v" .Data.x}} (
@@ -128,12 +128,12 @@ func isMultiLineStringCompare(x, y interface{}) bool {
 	return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
 	return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
 }
 }
 
 
-func multiLineDiffResult(diff string) Result {
+func multiLineDiffResult(diff string, x, y interface{}) Result {
 	return ResultFailureTemplate(`
 	return ResultFailureTemplate(`
 --- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}}
 --- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}}
 +++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}}
 +++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}}
 {{ .Data.diff }}`,
 {{ .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.
 // Len succeeds if the sequence has the expected length.

+ 5 - 0
vendor/gotest.tools/v3/assert/cmp/result.go

@@ -69,6 +69,11 @@ func (r templatedResult) FailureMessage(args []ast.Expr) string {
 	return msg
 	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
 // 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,
 // 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
 // the comparison args with the callArg function, and the formatNode function may

+ 14 - 11
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 are files in the ./testdata/ subdirectory of the package under test.
 Golden files can be automatically updated to match new values by running
 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.
 compare the diff of the old expected value to the new expected value.
 */
 */
 package golden // import "gotest.tools/v3/golden"
 package golden // import "gotest.tools/v3/golden"
@@ -18,9 +18,12 @@ import (
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert"
 	"gotest.tools/v3/assert/cmp"
 	"gotest.tools/v3/assert/cmp"
 	"gotest.tools/v3/internal/format"
 	"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 {
 type helperT interface {
 	Helper()
 	Helper()
@@ -28,7 +31,7 @@ type helperT interface {
 
 
 // NormalizeCRLFToLF enables end-of-line normalization for actual values passed
 // NormalizeCRLFToLF enables end-of-line normalization for actual values passed
 // to Assert and String, as well as the values saved to golden files with
 // 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
 // Defaults to true. If you use the core.autocrlf=true git setting on windows
 // you will need to set this to false.
 // 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.
 // The default value may change in a future major release.
 var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "false"
 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 {
 func FlagUpdate() bool {
-	return *flagUpdate
+	return source.Update
 }
 }
 
 
 // Open opens the file in ./testdata
 // 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.
 // 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.
 // to the golden file.
 //
 //
 // This is equivalent to assert.Assert(t, String(actual, filename))
 // 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
 // String compares actual to the contents of filename and returns success
 // if the strings are equal.
 // 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.
 // to the golden file.
 //
 //
 // Any \r\n substrings in actual are converted to a single \n character
 // 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 {
 func failurePostamble(filename string) string {
 	return fmt.Sprintf(`
 	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))
 `, Path(filename))
 }
 }
 
 
 // AssertBytes compares actual to the expected value in the golden.
 // 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.
 // to the golden file.
 //
 //
 // This is equivalent to assert.Assert(t, Bytes(actual, filename))
 // 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
 // Bytes compares actual to the contents of filename and returns success
 // if the bytes are equal.
 // 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.
 // to the golden file.
 func Bytes(actual []byte, filename string) cmp.Comparison {
 func Bytes(actual []byte, filename string) cmp.Comparison {
 	return func() cmp.Result {
 	return func() cmp.Result {
@@ -175,7 +178,7 @@ func compare(actual []byte, filename string) (cmp.Result, []byte) {
 }
 }
 
 
 func update(filename string, actual []byte) error {
 func update(filename string, actual []byte) error {
-	if !*flagUpdate {
+	if !source.Update {
 		return nil
 		return nil
 	}
 	}
 	if dir := filepath.Dir(Path(filename)); dir != "." {
 	if dir := filepath.Dir(Path(filename)); dir != "." {

+ 21 - 0
vendor/gotest.tools/v3/internal/assert/result.go

@@ -1,6 +1,7 @@
 package assert
 package assert
 
 
 import (
 import (
+	"errors"
 	"fmt"
 	"fmt"
 	"go/ast"
 	"go/ast"
 
 
@@ -25,6 +26,22 @@ func RunComparison(
 		return true
 		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
 	var message string
 	switch typed := result.(type) {
 	switch typed := result.(type) {
 	case resultWithComparisonArgs:
 	case resultWithComparisonArgs:
@@ -52,6 +69,10 @@ type resultBasic interface {
 	FailureMessage() string
 	FailureMessage() string
 }
 }
 
 
+type updateExpected interface {
+	UpdatedExpected(stackIndex int) error
+}
+
 // filterPrintableExpr filters the ast.Expr slice to only include Expr that are
 // filterPrintableExpr filters the ast.Expr slice to only include Expr that are
 // easy to read when printed and contain relevant information to an assertion.
 // easy to read when printed and contain relevant information to an assertion.
 //
 //

+ 1 - 1
vendor/gotest.tools/v3/internal/source/defers.go

@@ -28,7 +28,7 @@ func guessDefer(node ast.Node) (ast.Node, error) {
 	defers := collectDefers(node)
 	defers := collectDefers(node)
 	switch len(defers) {
 	switch len(defers) {
 	case 0:
 	case 0:
-		return nil, fmt.Errorf("failed to expression in defer")
+		return nil, fmt.Errorf("failed to find expression in defer")
 	case 1:
 	case 1:
 		return defers[0].Call, nil
 		return defers[0].Call, nil
 	default:
 	default:

+ 23 - 56
vendor/gotest.tools/v3/internal/source/source.go

@@ -10,12 +10,8 @@ import (
 	"go/token"
 	"go/token"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
-	"strconv"
-	"strings"
 )
 )
 
 
-const baseStackIndex = 1
-
 // FormattedCallExprArg returns the argument from an ast.CallExpr at the
 // FormattedCallExprArg returns the argument from an ast.CallExpr at the
 // index in the call stack. The argument is formatted using FormatNode.
 // index in the call stack. The argument is formatted using FormatNode.
 func FormattedCallExprArg(stackIndex int, argPos int) (string, error) {
 func FormattedCallExprArg(stackIndex int, argPos int) (string, error) {
@@ -32,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
 // CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at
 // the index in the call stack.
 // the index in the call stack.
 func CallExprArgs(stackIndex int) ([]ast.Expr, error) {
 func CallExprArgs(stackIndex int) ([]ast.Expr, error) {
-	_, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex)
+	_, filename, line, ok := runtime.Caller(stackIndex + 1)
 	if !ok {
 	if !ok {
 		return nil, errors.New("failed to get call stack")
 		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()
 	fileset := token.NewFileSet()
 	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
 	astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("failed to parse source file %s: %w", filename, err)
 		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 {
 	if node := scanToLine(fileset, astFile, lineNum); node != nil {
 		return node, nil
 		return node, nil
 	}
 	}
@@ -63,8 +57,7 @@ func getNodeAtLine(filename string, lineNum int) (ast.Node, error) {
 			return node, err
 			return node, err
 		}
 		}
 	}
 	}
-	return nil, fmt.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 {
 func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
@@ -73,7 +66,7 @@ func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
 		switch {
 		switch {
 		case node == nil || matchedNode != nil:
 		case node == nil || matchedNode != nil:
 			return false
 			return false
-		case nodePosition(fileset, node).Line == lineNum:
+		case fileset.Position(node.Pos()).Line == lineNum:
 			matchedNode = node
 			matchedNode = node
 			return false
 			return false
 		}
 		}
@@ -82,46 +75,17 @@ func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
 	return matchedNode
 	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{}
 	visitor := &callExprVisitor{}
 	ast.Walk(visitor, node)
 	ast.Walk(visitor, node)
 	if visitor.expr == nil {
 	if visitor.expr == nil {
@@ -172,6 +136,9 @@ type debugFormatNode struct {
 }
 }
 
 
 func (n debugFormatNode) String() string {
 func (n debugFormatNode) String() string {
+	if n.Node == nil {
+		return "none"
+	}
 	out, err := FormatNode(n.Node)
 	out, err := FormatNode(n.Node)
 	if err != nil {
 	if err != nil {
 		return fmt.Sprintf("failed to format %s: %s", n.Node, err)
 		return fmt.Sprintf("failed to format %s: %s", n.Node, err)

+ 138 - 0
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, ""
+}

+ 35 - 0
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
+}

+ 1 - 1
vendor/modules.txt

@@ -1105,7 +1105,7 @@ google.golang.org/protobuf/types/known/fieldmaskpb
 google.golang.org/protobuf/types/known/structpb
 google.golang.org/protobuf/types/known/structpb
 google.golang.org/protobuf/types/known/timestamppb
 google.golang.org/protobuf/types/known/timestamppb
 google.golang.org/protobuf/types/known/wrapperspb
 google.golang.org/protobuf/types/known/wrapperspb
-# gotest.tools/v3 v3.2.0
+# gotest.tools/v3 v3.3.0
 ## explicit; go 1.13
 ## explicit; go 1.13
 gotest.tools/v3/assert
 gotest.tools/v3/assert
 gotest.tools/v3/assert/cmp
 gotest.tools/v3/assert/cmp