which is not useful
+ case exp.Error == "" && exp.ExitCode != 0:
+ case exp.Error == "" && r.Error != nil:
+ add("Expected no error")
+ case exp.Error != "" && r.Error == nil:
+ add("Expected error to contain %q, but there was no error", exp.Error)
+ case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error):
+ add("Expected error to contain %q", exp.Error)
+ }
+
+ if len(errors) == 0 {
+ return nil
+ }
+ return fmt.Errorf("%s\nFailures:\n%s", r, strings.Join(errors, "\n"))
+}
+
+func matchOutput(expected string, actual string) bool {
+ switch expected {
+ case None:
+ return actual == ""
+ default:
+ return strings.Contains(actual, expected)
+ }
+}
+
+func (r *Result) String() string {
+ var timeout string
+ if r.Timeout {
+ timeout = " (timeout)"
+ }
+ var errString string
+ if r.Error != nil {
+ errString = "\nError: " + r.Error.Error()
+ }
+
+ return fmt.Sprintf(`
+Command: %s
+ExitCode: %d%s%s
+Stdout: %v
+Stderr: %v
+`,
+ strings.Join(r.Cmd.Args, " "),
+ r.ExitCode,
+ timeout,
+ errString,
+ r.Stdout(),
+ r.Stderr())
+}
+
+// Expected is the expected output from a Command. This struct is compared to a
+// Result struct by Result.Assert().
+type Expected struct {
+ ExitCode int
+ Timeout bool
+ Error string
+ Out string
+ Err string
+}
+
+// Success is the default expected result. A Success result is one with a 0
+// ExitCode.
+var Success = Expected{}
+
+// Stdout returns the stdout of the process as a string
+func (r *Result) Stdout() string {
+ return r.outBuffer.String()
+}
+
+// Stderr returns the stderr of the process as a string
+func (r *Result) Stderr() string {
+ return r.errBuffer.String()
+}
+
+// Combined returns the stdout and stderr combined into a single string
+func (r *Result) Combined() string {
+ return r.outBuffer.String() + r.errBuffer.String()
+}
+
+func (r *Result) setExitError(err error) {
+ if err == nil {
+ return
+ }
+ r.Error = err
+ r.ExitCode = processExitCode(err)
+}
+
+// Cmd contains the arguments and options for a process to run as part of a test
+// suite.
+type Cmd struct {
+ Command []string
+ Timeout time.Duration
+ Stdin io.Reader
+ Stdout io.Writer
+ Dir string
+ Env []string
+ ExtraFiles []*os.File
+}
+
+// Command create a simple Cmd with the specified command and arguments
+func Command(command string, args ...string) Cmd {
+ return Cmd{Command: append([]string{command}, args...)}
+}
+
+// RunCmd runs a command and returns a Result
+func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result {
+ for _, op := range cmdOperators {
+ op(&cmd)
+ }
+ result := StartCmd(cmd)
+ if result.Error != nil {
+ return result
+ }
+ return WaitOnCmd(cmd.Timeout, result)
+}
+
+// RunCommand runs a command with default options, and returns a result
+func RunCommand(command string, args ...string) *Result {
+ return RunCmd(Command(command, args...))
+}
+
+// StartCmd starts a command, but doesn't wait for it to finish
+func StartCmd(cmd Cmd) *Result {
+ result := buildCmd(cmd)
+ if result.Error != nil {
+ return result
+ }
+ result.setExitError(result.Cmd.Start())
+ return result
+}
+
+// TODO: support exec.CommandContext
+func buildCmd(cmd Cmd) *Result {
+ var execCmd *exec.Cmd
+ switch len(cmd.Command) {
+ case 1:
+ execCmd = exec.Command(cmd.Command[0])
+ default:
+ execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...)
+ }
+ outBuffer := new(lockedBuffer)
+ errBuffer := new(lockedBuffer)
+
+ execCmd.Stdin = cmd.Stdin
+ execCmd.Dir = cmd.Dir
+ execCmd.Env = cmd.Env
+ if cmd.Stdout != nil {
+ execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout)
+ } else {
+ execCmd.Stdout = outBuffer
+ }
+ execCmd.Stderr = errBuffer
+ execCmd.ExtraFiles = cmd.ExtraFiles
+
+ return &Result{
+ Cmd: execCmd,
+ outBuffer: outBuffer,
+ errBuffer: errBuffer,
+ }
+}
+
+// WaitOnCmd waits for a command to complete. If timeout is non-nil then
+// only wait until the timeout.
+func WaitOnCmd(timeout time.Duration, result *Result) *Result {
+ if timeout == time.Duration(0) {
+ result.setExitError(result.Cmd.Wait())
+ return result
+ }
+
+ done := make(chan error, 1)
+ // Wait for command to exit in a goroutine
+ go func() {
+ done <- result.Cmd.Wait()
+ }()
+
+ select {
+ case <-time.After(timeout):
+ killErr := result.Cmd.Process.Kill()
+ if killErr != nil {
+ fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr)
+ }
+ result.Timeout = true
+ case err := <-done:
+ result.setExitError(err)
+ }
+ return result
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go
new file mode 100644
index 0000000..1a5fef9
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go
@@ -0,0 +1,181 @@
+package icmd
+
+import (
+ "bytes"
+ "errors"
+ "os"
+ "path/filepath"
+ "runtime"
+ "testing"
+ "time"
+
+ exec "golang.org/x/sys/execabs"
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/fs"
+ "gotest.tools/v3/internal/maint"
+)
+
+var (
+ bindir = fs.NewDir(maint.T, "icmd-dir")
+ binname = bindir.Join("bin-stub") + pathext()
+ stubpath = filepath.FromSlash("./internal/stub")
+)
+
+func pathext() string {
+ if runtime.GOOS == "windows" {
+ return ".exe"
+ }
+ return ""
+}
+
+func TestMain(m *testing.M) {
+ exitcode := m.Run()
+ bindir.Remove()
+ os.Exit(exitcode)
+}
+
+func buildStub(t assert.TestingT) {
+ if _, err := os.Stat(binname); err == nil {
+ return
+ }
+ result := RunCommand("go", "build", "-o", binname, stubpath)
+ result.Assert(t, Success)
+}
+
+func TestRunCommandSuccess(t *testing.T) {
+ buildStub(t)
+
+ result := RunCommand(binname)
+ result.Assert(t, Success)
+}
+
+func TestRunCommandWithCombined(t *testing.T) {
+ buildStub(t)
+
+ result := RunCommand(binname, "-warn")
+ result.Assert(t, Expected{})
+
+ assert.Equal(t, result.Combined(), "this is stdout\nthis is stderr\n")
+ assert.Equal(t, result.Stdout(), "this is stdout\n")
+ assert.Equal(t, result.Stderr(), "this is stderr\n")
+}
+
+func TestRunCommandWithTimeoutFinished(t *testing.T) {
+ buildStub(t)
+
+ result := RunCmd(Cmd{
+ Command: []string{binname, "-sleep=1ms"},
+ Timeout: 2 * time.Second,
+ })
+ result.Assert(t, Expected{Out: "this is stdout"})
+}
+
+func TestRunCommandWithTimeoutKilled(t *testing.T) {
+ buildStub(t)
+
+ command := []string{binname, "-sleep=200ms"}
+ result := RunCmd(Cmd{Command: command, Timeout: 30 * time.Millisecond})
+ result.Assert(t, Expected{Timeout: true, Out: None, Err: None})
+}
+
+func TestRunCommandWithErrors(t *testing.T) {
+ buildStub(t)
+
+ result := RunCommand("doesnotexists")
+ expected := `exec: "doesnotexists": executable file not found`
+ result.Assert(t, Expected{Out: None, Err: None, ExitCode: 127, Error: expected})
+}
+
+func TestRunCommandWithStdoutNoStderr(t *testing.T) {
+ buildStub(t)
+
+ result := RunCommand(binname)
+ result.Assert(t, Expected{Out: "this is stdout\n", Err: None})
+}
+
+func TestRunCommandWithExitCode(t *testing.T) {
+ buildStub(t)
+
+ result := RunCommand(binname, "-fail=99")
+ result.Assert(t, Expected{
+ ExitCode: 99,
+ Error: "exit status 99",
+ })
+}
+
+func TestResult_Match_NotMatched(t *testing.T) {
+ result := &Result{
+ Cmd: exec.Command("binary", "arg1"),
+ ExitCode: 99,
+ Error: errors.New("exit code 99"),
+ outBuffer: newLockedBuffer("the output"),
+ errBuffer: newLockedBuffer("the stderr"),
+ Timeout: true,
+ }
+ exp := Expected{
+ ExitCode: 101,
+ Out: "Something else",
+ Err: None,
+ }
+ err := result.match(exp)
+ assert.ErrorContains(t, err, "Failures")
+ assert.Equal(t, err.Error(), expectedMatch)
+}
+
+var expectedMatch = `
+Command: binary arg1
+ExitCode: 99 (timeout)
+Error: exit code 99
+Stdout: the output
+Stderr: the stderr
+
+Failures:
+ExitCode was 99 expected 101
+Expected command to finish, but it hit the timeout
+Expected stdout to contain "Something else"
+Expected stderr to contain "[NOTHING]"`
+
+func newLockedBuffer(s string) *lockedBuffer {
+ return &lockedBuffer{buf: *bytes.NewBufferString(s)}
+}
+
+func TestResult_Match_NotMatchedNoError(t *testing.T) {
+ result := &Result{
+ Cmd: exec.Command("binary", "arg1"),
+ outBuffer: newLockedBuffer("the output"),
+ errBuffer: newLockedBuffer("the stderr"),
+ }
+ exp := Expected{
+ ExitCode: 101,
+ Out: "Something else",
+ Err: None,
+ }
+ err := result.match(exp)
+ assert.ErrorContains(t, err, "Failures")
+ assert.Equal(t, err.Error(), expectedResultMatchNoMatch)
+}
+
+var expectedResultMatchNoMatch = `
+Command: binary arg1
+ExitCode: 0
+Stdout: the output
+Stderr: the stderr
+
+Failures:
+ExitCode was 0 expected 101
+Expected stdout to contain "Something else"
+Expected stderr to contain "[NOTHING]"`
+
+func TestResult_Match_Match(t *testing.T) {
+ result := &Result{
+ Cmd: exec.Command("binary", "arg1"),
+ outBuffer: newLockedBuffer("the output"),
+ errBuffer: newLockedBuffer("the stderr"),
+ }
+ exp := Expected{
+ Out: "the output",
+ Err: "the stderr",
+ }
+ err := result.match(exp)
+ assert.NilError(t, err)
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go
new file mode 100644
index 0000000..216b82d
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go
@@ -0,0 +1,22 @@
+package icmd_test
+
+import (
+ "testing"
+
+ "gotest.tools/v3/icmd"
+)
+
+var t = &testing.T{}
+
+func ExampleRunCommand() {
+ result := icmd.RunCommand("bash", "-c", "echo all good")
+ result.Assert(t, icmd.Success)
+}
+
+func ExampleRunCmd() {
+ result := icmd.RunCmd(icmd.Command("cat", "/does/not/exist"))
+ result.Assert(t, icmd.Expected{
+ ExitCode: 1,
+ Err: "cat: /does/not/exist: No such file or directory",
+ })
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go
new file mode 100644
index 0000000..2e98f86
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go
@@ -0,0 +1,24 @@
+package icmd
+
+import (
+ "errors"
+
+ exec "golang.org/x/sys/execabs"
+)
+
+func processExitCode(err error) int {
+ if err == nil {
+ return 0
+ }
+
+ 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 127
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go
new file mode 100644
index 0000000..a7fa1fd
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "time"
+)
+
+func main() {
+ sleep := flag.Duration("sleep", 0, "Sleep")
+ warn := flag.Bool("warn", false, "Warn")
+ fail := flag.Int("fail", 0, "Fail with code")
+ flag.Parse()
+
+ if *sleep != 0 {
+ time.Sleep(*sleep)
+ }
+
+ fmt.Println("this is stdout")
+ if *warn {
+ fmt.Fprintln(os.Stderr, "this is stderr")
+ }
+
+ os.Exit(*fail)
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go
new file mode 100644
index 0000000..35c3958
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go
@@ -0,0 +1,46 @@
+package icmd
+
+import (
+ "io"
+ "os"
+ "time"
+)
+
+// CmdOp is an operation which modified a Cmd structure used to execute commands
+type CmdOp func(*Cmd)
+
+// WithTimeout sets the timeout duration of the command
+func WithTimeout(timeout time.Duration) CmdOp {
+ return func(c *Cmd) {
+ c.Timeout = timeout
+ }
+}
+
+// WithEnv sets the environment variable of the command.
+// Each arguments are in the form of KEY=VALUE
+func WithEnv(env ...string) CmdOp {
+ return func(c *Cmd) {
+ c.Env = env
+ }
+}
+
+// Dir sets the working directory of the command
+func Dir(path string) CmdOp {
+ return func(c *Cmd) {
+ c.Dir = path
+ }
+}
+
+// WithStdin sets the standard input of the command to the specified reader
+func WithStdin(r io.Reader) CmdOp {
+ return func(c *Cmd) {
+ c.Stdin = r
+ }
+}
+
+// WithExtraFile adds a file descriptor to the command
+func WithExtraFile(f *os.File) CmdOp {
+ return func(c *Cmd) {
+ c.ExtraFiles = append(c.ExtraFiles, f)
+ }
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go
new file mode 100644
index 0000000..0d67751
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go
@@ -0,0 +1,160 @@
+package assert
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "reflect"
+
+ "gotest.tools/v3/assert/cmp"
+ "gotest.tools/v3/internal/format"
+ "gotest.tools/v3/internal/source"
+)
+
+// LogT is the subset of testing.T used by the assert package.
+type LogT interface {
+ Log(args ...interface{})
+}
+
+type helperT interface {
+ Helper()
+}
+
+const failureMessage = "assertion failed: "
+
+// Eval the comparison and print a failure messages if the comparison has failed.
+func Eval(
+ t LogT,
+ argSelector argSelector,
+ comparison interface{},
+ msgAndArgs ...interface{},
+) bool {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ var success bool
+ switch check := comparison.(type) {
+ case bool:
+ if check {
+ return true
+ }
+ logFailureFromBool(t, msgAndArgs...)
+
+ // Undocumented legacy comparison without Result type
+ case func() (success bool, message string):
+ success = runCompareFunc(t, check, msgAndArgs...)
+
+ case nil:
+ return true
+
+ case error:
+ msg := failureMsgFromError(check)
+ t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
+
+ case cmp.Comparison:
+ success = RunComparison(t, argSelector, check, msgAndArgs...)
+
+ case func() cmp.Result:
+ success = RunComparison(t, argSelector, check, msgAndArgs...)
+
+ default:
+ t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
+ }
+ return success
+}
+
+func runCompareFunc(
+ t LogT,
+ f func() (success bool, message string),
+ msgAndArgs ...interface{},
+) bool {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ if success, message := f(); !success {
+ t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
+ return false
+ }
+ return true
+}
+
+func logFailureFromBool(t LogT, msgAndArgs ...interface{}) {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ const stackIndex = 3 // Assert()/Check(), assert(), logFailureFromBool()
+ args, err := source.CallExprArgs(stackIndex)
+ if err != nil {
+ t.Log(err.Error())
+ return
+ }
+
+ const comparisonArgIndex = 1 // Assert(t, comparison)
+ if len(args) <= comparisonArgIndex {
+ t.Log(failureMessage + "but assert failed to find the expression to print")
+ return
+ }
+
+ msg, err := boolFailureMessage(args[comparisonArgIndex])
+ if err != nil {
+ t.Log(err.Error())
+ msg = "expression is false"
+ }
+
+ t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
+}
+
+func failureMsgFromError(err error) string {
+ // Handle errors with non-nil types
+ v := reflect.ValueOf(err)
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ return fmt.Sprintf("error is not nil: error has type %T", err)
+ }
+ return "error is not nil: " + err.Error()
+}
+
+func boolFailureMessage(expr ast.Expr) (string, error) {
+ if binaryExpr, ok := expr.(*ast.BinaryExpr); ok {
+ x, err := source.FormatNode(binaryExpr.X)
+ if err != nil {
+ return "", err
+ }
+ y, err := source.FormatNode(binaryExpr.Y)
+ if err != nil {
+ return "", err
+ }
+
+ 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 {
+ x, err := source.FormatNode(unaryExpr.X)
+ if err != nil {
+ return "", err
+ }
+ 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
+ }
+ return "expression is false: " + formatted, nil
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go
new file mode 100644
index 0000000..3603206
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go
@@ -0,0 +1,146 @@
+package assert
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+
+ "gotest.tools/v3/assert/cmp"
+ "gotest.tools/v3/internal/format"
+ "gotest.tools/v3/internal/source"
+)
+
+// RunComparison and return Comparison.Success. If the comparison fails a messages
+// will be printed using t.Log.
+func RunComparison(
+ t LogT,
+ argSelector argSelector,
+ f cmp.Comparison,
+ msgAndArgs ...interface{},
+) bool {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ result := f()
+ if result.Success() {
+ 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:
+ const stackIndex = 3 // Assert/Check, assert, RunComparison
+ args, err := source.CallExprArgs(stackIndex)
+ if err != nil {
+ t.Log(err.Error())
+ }
+ message = typed.FailureMessage(filterPrintableExpr(argSelector(args)))
+ case resultBasic:
+ message = typed.FailureMessage()
+ default:
+ message = fmt.Sprintf("comparison returned invalid Result type: %T", result)
+ }
+
+ t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
+ return false
+}
+
+type resultWithComparisonArgs interface {
+ FailureMessage(args []ast.Expr) string
+}
+
+type resultBasic interface {
+ FailureMessage() string
+}
+
+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.
+//
+// Ident and SelectorExpr are included because they print nicely and the variable
+// names may provide additional context to their values.
+// BasicLit and CompositeLit are excluded because their source is equivalent to
+// their value, which is already available.
+// Other types are ignored for now, but could be added if they are relevant.
+func filterPrintableExpr(args []ast.Expr) []ast.Expr {
+ result := make([]ast.Expr, len(args))
+ for i, arg := range args {
+ if isShortPrintableExpr(arg) {
+ result[i] = arg
+ continue
+ }
+
+ if starExpr, ok := arg.(*ast.StarExpr); ok {
+ result[i] = starExpr.X
+ continue
+ }
+ }
+ return result
+}
+
+func isShortPrintableExpr(expr ast.Expr) bool {
+ switch expr.(type) {
+ case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr:
+ return true
+ case *ast.BinaryExpr, *ast.UnaryExpr:
+ return true
+ default:
+ // CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr
+ return false
+ }
+}
+
+type argSelector func([]ast.Expr) []ast.Expr
+
+// ArgsAfterT selects args starting at position 1. Used when the caller has a
+// testing.T as the first argument, and the args to select should follow it.
+func ArgsAfterT(args []ast.Expr) []ast.Expr {
+ if len(args) < 1 {
+ return nil
+ }
+ return args[1:]
+}
+
+// ArgsFromComparisonCall selects args from the CallExpression at position 1.
+// Used when the caller has a testing.T as the first argument, and the args to
+// select are passed to the cmp.Comparison at position 1.
+func ArgsFromComparisonCall(args []ast.Expr) []ast.Expr {
+ if len(args) <= 1 {
+ return nil
+ }
+ if callExpr, ok := args[1].(*ast.CallExpr); ok {
+ return callExpr.Args
+ }
+ return nil
+}
+
+// ArgsAtZeroIndex selects args from the CallExpression at position 1.
+// Used when the caller accepts a single cmp.Comparison argument.
+func ArgsAtZeroIndex(args []ast.Expr) []ast.Expr {
+ if len(args) == 0 {
+ return nil
+ }
+ if callExpr, ok := args[0].(*ast.CallExpr); ok {
+ return callExpr.Args
+ }
+ return nil
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go
new file mode 100644
index 0000000..58206e5
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go
@@ -0,0 +1,48 @@
+/*Package cleanup handles migration to and support for the Go 1.14+
+testing.TB.Cleanup() function.
+*/
+package cleanup
+
+import (
+ "os"
+ "strings"
+)
+
+type cleanupT interface {
+ Cleanup(f func())
+}
+
+// implemented by gotest.tools/x/subtest.TestContext
+type addCleanupT interface {
+ AddCleanup(f func())
+}
+
+type logT interface {
+ Log(...interface{})
+}
+
+type helperT interface {
+ Helper()
+}
+
+var noCleanup = strings.ToLower(os.Getenv("TEST_NOCLEANUP")) == "true"
+
+// Cleanup registers f as a cleanup function on t if any mechanisms are available.
+//
+// Skips registering f if TEST_NOCLEANUP is set to true.
+func Cleanup(t logT, f func()) {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ if noCleanup {
+ t.Log("skipping cleanup because TEST_NOCLEANUP was enabled.")
+ return
+ }
+ if ct, ok := t.(cleanupT); ok {
+ ct.Cleanup(f)
+ return
+ }
+ if tc, ok := t.(addCleanupT); ok {
+ tc.AddCleanup(f)
+ }
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE
new file mode 100644
index 0000000..c67dad6
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Patrick Mezard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+ The names of its contributors may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go
new file mode 100644
index 0000000..9bf506b
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go
@@ -0,0 +1,423 @@
+/*Package difflib is a partial port of Python difflib module.
+
+Original source: https://github.com/pmezard/go-difflib
+
+This file is trimmed to only the parts used by this repository.
+*/
+package difflib // import "gotest.tools/v3/internal/difflib"
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+// Match stores line numbers of size of match
+type Match struct {
+ A int
+ B int
+ Size int
+}
+
+// OpCode identifies the type of diff
+type OpCode struct {
+ Tag byte
+ I1 int
+ I2 int
+ J1 int
+ J2 int
+}
+
+// SequenceMatcher compares sequence of strings. The basic
+// algorithm predates, and is a little fancier than, an algorithm
+// published in the late 1980's by Ratcliff and Obershelp under the
+// hyperbolic name "gestalt pattern matching". The basic idea is to find
+// the longest contiguous matching subsequence that contains no "junk"
+// elements (R-O doesn't address junk). The same idea is then applied
+// recursively to the pieces of the sequences to the left and to the right
+// of the matching subsequence. This does not yield minimal edit
+// sequences, but does tend to yield matches that "look right" to people.
+//
+// SequenceMatcher tries to compute a "human-friendly diff" between two
+// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
+// longest *contiguous* & junk-free matching subsequence. That's what
+// catches peoples' eyes. The Windows(tm) windiff has another interesting
+// notion, pairing up elements that appear uniquely in each sequence.
+// That, and the method here, appear to yield more intuitive difference
+// reports than does diff. This method appears to be the least vulnerable
+// to synching up on blocks of "junk lines", though (like blank lines in
+// ordinary text files, or maybe "" lines in HTML files). That may be
+// because this is the only method of the 3 that has a *concept* of
+// "junk" .
+//
+// Timing: Basic R-O is cubic time worst case and quadratic time expected
+// case. SequenceMatcher is quadratic time for the worst case and has
+// expected-case behavior dependent in a complicated way on how many
+// elements the sequences have in common; best case time is linear.
+type SequenceMatcher struct {
+ a []string
+ b []string
+ b2j map[string][]int
+ IsJunk func(string) bool
+ autoJunk bool
+ bJunk map[string]struct{}
+ matchingBlocks []Match
+ fullBCount map[string]int
+ bPopular map[string]struct{}
+ opCodes []OpCode
+}
+
+// NewMatcher returns a new SequenceMatcher
+func NewMatcher(a, b []string) *SequenceMatcher {
+ m := SequenceMatcher{autoJunk: true}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+// SetSeqs sets two sequences to be compared.
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
+ m.SetSeq1(a)
+ m.SetSeq2(b)
+}
+
+// SetSeq1 sets the first sequence to be compared. The second sequence to be compared is
+// not changed.
+//
+// SequenceMatcher computes and caches detailed information about the second
+// sequence, so if you want to compare one sequence S against many sequences,
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
+// sequences.
+//
+// See also SetSeqs() and SetSeq2().
+func (m *SequenceMatcher) SetSeq1(a []string) {
+ if &a == &m.a {
+ return
+ }
+ m.a = a
+ m.matchingBlocks = nil
+ m.opCodes = nil
+}
+
+// SetSeq2 sets the second sequence to be compared. The first sequence to be compared is
+// not changed.
+func (m *SequenceMatcher) SetSeq2(b []string) {
+ if &b == &m.b {
+ return
+ }
+ m.b = b
+ m.matchingBlocks = nil
+ m.opCodes = nil
+ m.fullBCount = nil
+ m.chainB()
+}
+
+func (m *SequenceMatcher) chainB() {
+ // Populate line -> index mapping
+ b2j := map[string][]int{}
+ for i, s := range m.b {
+ indices := b2j[s]
+ indices = append(indices, i)
+ b2j[s] = indices
+ }
+
+ // Purge junk elements
+ m.bJunk = map[string]struct{}{}
+ if m.IsJunk != nil {
+ junk := m.bJunk
+ for s := range b2j {
+ if m.IsJunk(s) {
+ junk[s] = struct{}{}
+ }
+ }
+ for s := range junk {
+ delete(b2j, s)
+ }
+ }
+
+ // Purge remaining popular elements
+ popular := map[string]struct{}{}
+ n := len(m.b)
+ if m.autoJunk && n >= 200 {
+ ntest := n/100 + 1
+ for s, indices := range b2j {
+ if len(indices) > ntest {
+ popular[s] = struct{}{}
+ }
+ }
+ for s := range popular {
+ delete(b2j, s)
+ }
+ }
+ m.bPopular = popular
+ m.b2j = b2j
+}
+
+func (m *SequenceMatcher) isBJunk(s string) bool {
+ _, ok := m.bJunk[s]
+ return ok
+}
+
+// Find longest matching block in a[alo:ahi] and b[blo:bhi].
+//
+// If IsJunk is not defined:
+//
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
+// alo <= i <= i+k <= ahi
+// blo <= j <= j+k <= bhi
+// and for all (i',j',k') meeting those conditions,
+// k >= k'
+// i <= i'
+// and if i == i', j <= j'
+//
+// In other words, of all maximal matching blocks, return one that
+// starts earliest in a, and of all those maximal matching blocks that
+// start earliest in a, return the one that starts earliest in b.
+//
+// If IsJunk is defined, first the longest matching block is
+// determined as above, but with the additional restriction that no
+// junk element appears in the block. Then that block is extended as
+// far as possible by matching (only) junk elements on both sides. So
+// the resulting block never matches on junk except as identical junk
+// happens to be adjacent to an "interesting" match.
+//
+// If no blocks match, return (alo, blo, 0).
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
+ // CAUTION: stripping common prefix or suffix would be incorrect.
+ // E.g.,
+ // ab
+ // acab
+ // Longest matching block is "ab", but if common prefix is
+ // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
+ // strip, so ends up claiming that ab is changed to acab by
+ // inserting "ca" in the middle. That's minimal but unintuitive:
+ // "it's obvious" that someone inserted "ac" at the front.
+ // Windiff ends up at the same place as diff, but by pairing up
+ // the unique 'b's and then matching the first two 'a's.
+ besti, bestj, bestsize := alo, blo, 0
+
+ // find longest junk-free match
+ // during an iteration of the loop, j2len[j] = length of longest
+ // junk-free match ending with a[i-1] and b[j]
+ j2len := map[int]int{}
+ for i := alo; i != ahi; i++ {
+ // look at all instances of a[i] in b; note that because
+ // b2j has no junk keys, the loop is skipped if a[i] is junk
+ newj2len := map[int]int{}
+ for _, j := range m.b2j[m.a[i]] {
+ // a[i] matches b[j]
+ if j < blo {
+ continue
+ }
+ if j >= bhi {
+ break
+ }
+ k := j2len[j-1] + 1
+ newj2len[j] = k
+ if k > bestsize {
+ besti, bestj, bestsize = i-k+1, j-k+1, k
+ }
+ }
+ j2len = newj2len
+ }
+
+ // Extend the best by non-junk elements on each end. In particular,
+ // "popular" non-junk elements aren't in b2j, which greatly speeds
+ // the inner loop above, but also means "the best" match so far
+ // doesn't contain any junk *or* popular non-junk elements.
+ for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ !m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ // Now that we have a wholly interesting match (albeit possibly
+ // empty!), we may as well suck up the matching junk on each
+ // side of it too. Can't think of a good reason not to, and it
+ // saves post-processing the (possibly considerable) expense of
+ // figuring out what to do with it. In the case of an empty
+ // interesting match, this is clearly the right thing to do,
+ // because no other kind of match is possible in the regions.
+ for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ return Match{A: besti, B: bestj, Size: bestsize}
+}
+
+// GetMatchingBlocks returns a list of triples describing matching subsequences.
+//
+// Each triple is of the form (i, j, n), and means that
+// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
+// adjacent triples in the list, and the second is not the last triple in the
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
+// adjacent equal blocks.
+//
+// The last triple is a dummy, (len(a), len(b), 0), and is the only
+// triple with n==0.
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
+ if m.matchingBlocks != nil {
+ return m.matchingBlocks
+ }
+
+ var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
+ matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
+ match := m.findLongestMatch(alo, ahi, blo, bhi)
+ i, j, k := match.A, match.B, match.Size
+ if match.Size > 0 {
+ if alo < i && blo < j {
+ matched = matchBlocks(alo, i, blo, j, matched)
+ }
+ matched = append(matched, match)
+ if i+k < ahi && j+k < bhi {
+ matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
+ }
+ }
+ return matched
+ }
+ matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
+
+ // It's possible that we have adjacent equal blocks in the
+ // matching_blocks list now.
+ nonAdjacent := []Match{}
+ i1, j1, k1 := 0, 0, 0
+ for _, b := range matched {
+ // Is this block adjacent to i1, j1, k1?
+ i2, j2, k2 := b.A, b.B, b.Size
+ if i1+k1 == i2 && j1+k1 == j2 {
+ // Yes, so collapse them -- this just increases the length of
+ // the first block by the length of the second, and the first
+ // block so lengthened remains the block to compare against.
+ k1 += k2
+ } else {
+ // Not adjacent. Remember the first block (k1==0 means it's
+ // the dummy we started with), and make the second block the
+ // new block to compare against.
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+ i1, j1, k1 = i2, j2, k2
+ }
+ }
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+
+ nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
+ m.matchingBlocks = nonAdjacent
+ return m.matchingBlocks
+}
+
+// GetOpCodes returns a list of 5-tuples describing how to turn a into b.
+//
+// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
+// tuple preceding it, and likewise for j1 == the previous j2.
+//
+// The tags are characters, with these meanings:
+//
+// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
+//
+// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
+//
+// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
+//
+// 'e' (equal): a[i1:i2] == b[j1:j2]
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
+ if m.opCodes != nil {
+ return m.opCodes
+ }
+ i, j := 0, 0
+ matching := m.GetMatchingBlocks()
+ opCodes := make([]OpCode, 0, len(matching))
+ for _, m := range matching {
+ // invariant: we've pumped out correct diffs to change
+ // a[:i] into b[:j], and the next matching block is
+ // a[ai:ai+size] == b[bj:bj+size]. So we need to pump
+ // out a diff to change a[i:ai] into b[j:bj], pump out
+ // the matching block, and move (i,j) beyond the match
+ ai, bj, size := m.A, m.B, m.Size
+ tag := byte(0)
+ if i < ai && j < bj {
+ tag = 'r'
+ } else if i < ai {
+ tag = 'd'
+ } else if j < bj {
+ tag = 'i'
+ }
+ if tag > 0 {
+ opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
+ }
+ i, j = ai+size, bj+size
+ // the list of matching blocks is terminated by a
+ // sentinel with size 0
+ if size > 0 {
+ opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
+ }
+ }
+ m.opCodes = opCodes
+ return m.opCodes
+}
+
+// GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes.
+//
+// Return a generator of groups with up to n lines of context.
+// Each group is in the same format as returned by GetOpCodes().
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
+ if n < 0 {
+ n = 3
+ }
+ codes := m.GetOpCodes()
+ if len(codes) == 0 {
+ codes = []OpCode{{'e', 0, 1, 0, 1}}
+ }
+ // Fixup leading and trailing groups if they show no changes.
+ if codes[0].Tag == 'e' {
+ c := codes[0]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
+ }
+ if codes[len(codes)-1].Tag == 'e' {
+ c := codes[len(codes)-1]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
+ }
+ nn := n + n
+ groups := [][]OpCode{}
+ group := []OpCode{}
+ for _, c := range codes {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ // End the current group and start a new one whenever
+ // there is a large range with no changes.
+ if c.Tag == 'e' && i2-i1 > nn {
+ group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
+ j1, min(j2, j1+n)})
+ groups = append(groups, group)
+ group = []OpCode{}
+ i1, j1 = max(i1, i2-n), max(j1, j2-n)
+ }
+ group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
+ }
+ if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
+ groups = append(groups, group)
+ }
+ return groups
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go
new file mode 100644
index 0000000..9897d4b
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go
@@ -0,0 +1,161 @@
+package format
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "unicode"
+
+ "gotest.tools/v3/internal/difflib"
+)
+
+const (
+ contextLines = 2
+)
+
+// DiffConfig for a unified diff
+type DiffConfig struct {
+ A string
+ B string
+ From string
+ To string
+}
+
+// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better
+// support for showing the whitespace differences.
+func UnifiedDiff(conf DiffConfig) string {
+ a := strings.SplitAfter(conf.A, "\n")
+ b := strings.SplitAfter(conf.B, "\n")
+ groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines)
+ if len(groups) == 0 {
+ return ""
+ }
+
+ buf := new(bytes.Buffer)
+ writeFormat := func(format string, args ...interface{}) {
+ buf.WriteString(fmt.Sprintf(format, args...))
+ }
+ writeLine := func(prefix string, s string) {
+ buf.WriteString(prefix + s)
+ }
+ if hasWhitespaceDiffLines(groups, a, b) {
+ writeLine = visibleWhitespaceLine(writeLine)
+ }
+ formatHeader(writeFormat, conf)
+ for _, group := range groups {
+ formatRangeLine(writeFormat, group)
+ for _, opCode := range group {
+ in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2]
+ switch opCode.Tag {
+ case 'e':
+ formatLines(writeLine, " ", in)
+ case 'r':
+ formatLines(writeLine, "-", in)
+ formatLines(writeLine, "+", out)
+ case 'd':
+ formatLines(writeLine, "-", in)
+ case 'i':
+ formatLines(writeLine, "+", out)
+ }
+ }
+ }
+ return buf.String()
+}
+
+// hasWhitespaceDiffLines returns true if any diff groups is only different
+// because of whitespace characters.
+func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool {
+ for _, group := range groups {
+ in, out := new(bytes.Buffer), new(bytes.Buffer)
+ for _, opCode := range group {
+ if opCode.Tag == 'e' {
+ continue
+ }
+ for _, line := range a[opCode.I1:opCode.I2] {
+ in.WriteString(line)
+ }
+ for _, line := range b[opCode.J1:opCode.J2] {
+ out.WriteString(line)
+ }
+ }
+ if removeWhitespace(in.String()) == removeWhitespace(out.String()) {
+ return true
+ }
+ }
+ return false
+}
+
+func removeWhitespace(s string) string {
+ var result []rune
+ for _, r := range s {
+ if !unicode.IsSpace(r) {
+ result = append(result, r)
+ }
+ }
+ return string(result)
+}
+
+func visibleWhitespaceLine(ws func(string, string)) func(string, string) {
+ mapToVisibleSpace := func(r rune) rune {
+ switch r {
+ case '\n':
+ case ' ':
+ return '·'
+ case '\t':
+ return '▷'
+ case '\v':
+ return '▽'
+ case '\r':
+ return '↵'
+ case '\f':
+ return '↓'
+ default:
+ if unicode.IsSpace(r) {
+ return '�'
+ }
+ }
+ return r
+ }
+ return func(prefix, s string) {
+ ws(prefix, strings.Map(mapToVisibleSpace, s))
+ }
+}
+
+func formatHeader(wf func(string, ...interface{}), conf DiffConfig) {
+ if conf.From != "" || conf.To != "" {
+ wf("--- %s\n", conf.From)
+ wf("+++ %s\n", conf.To)
+ }
+}
+
+func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) {
+ first, last := group[0], group[len(group)-1]
+ range1 := formatRangeUnified(first.I1, last.I2)
+ range2 := formatRangeUnified(first.J1, last.J2)
+ wf("@@ -%s +%s @@\n", range1, range2)
+}
+
+// Convert range to the "ed" format
+func formatRangeUnified(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ if length == 0 {
+ beginning-- // empty ranges begin at line just before the range
+ }
+ return fmt.Sprintf("%d,%d", beginning, length)
+}
+
+func formatLines(writeLine func(string, string), prefix string, lines []string) {
+ for _, line := range lines {
+ writeLine(prefix, line)
+ }
+ // Add a newline if the last line is missing one so that the diff displays
+ // properly.
+ if !strings.HasSuffix(lines[len(lines)-1], "\n") {
+ writeLine("", "\n")
+ }
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go
new file mode 100644
index 0000000..4986635
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go
@@ -0,0 +1,71 @@
+package format_test
+
+import (
+ "testing"
+
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/golden"
+ "gotest.tools/v3/internal/format"
+)
+
+func TestUnifiedDiff(t *testing.T) {
+ var testcases = []struct {
+ name string
+ a string
+ b string
+ expected string
+ from string
+ to string
+ }{
+ {
+ name: "empty diff",
+ a: "a\nb\nc",
+ b: "a\nb\nc",
+ from: "from",
+ to: "to",
+ },
+ {
+ name: "one diff with header",
+ a: "a\nxyz\nc",
+ b: "a\nb\nc",
+ from: "from",
+ to: "to",
+ expected: "one-diff-with-header.golden",
+ },
+ {
+ name: "many diffs",
+ a: "a123\nxyz\nc\nbaba\nz\nt\nj2j2\nok\nok\ndone\n",
+ b: "a123\nxyz\nc\nabab\nz\nt\nj2j2\nok\nok\n",
+ expected: "many-diff.golden",
+ },
+ {
+ name: "no trailing newline",
+ a: "a123\nxyz\nc\nbaba\nz\nt\nj2j2\nok\nok\ndone\n",
+ b: "a123\nxyz\nc\nabab\nz\nt\nj2j2\nok\nok",
+ expected: "many-diff-no-trailing-newline.golden",
+ },
+ {
+ name: "whitespace diff",
+ a: " something\n something\n \v\r\n",
+ b: " something\n\tsomething\n \n",
+ expected: "whitespace-diff.golden",
+ },
+ }
+
+ for _, testcase := range testcases {
+ t.Run(testcase.name, func(t *testing.T) {
+ diff := format.UnifiedDiff(format.DiffConfig{
+ A: testcase.a,
+ B: testcase.b,
+ From: testcase.from,
+ To: testcase.to,
+ })
+
+ if testcase.expected != "" {
+ assert.Assert(t, golden.String(diff, testcase.expected))
+ return
+ }
+ assert.Equal(t, diff, "")
+ })
+ }
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go
new file mode 100644
index 0000000..5097e4b
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go
@@ -0,0 +1,27 @@
+package format // import "gotest.tools/v3/internal/format"
+
+import "fmt"
+
+// Message accepts a msgAndArgs varargs and formats it using fmt.Sprintf
+func Message(msgAndArgs ...interface{}) string {
+ switch len(msgAndArgs) {
+ case 0:
+ return ""
+ case 1:
+ return fmt.Sprintf("%v", msgAndArgs[0])
+ default:
+ return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
+ }
+}
+
+// WithCustomMessage accepts one or two messages and formats them appropriately
+func WithCustomMessage(source string, msgAndArgs ...interface{}) string {
+ custom := Message(msgAndArgs...)
+ switch {
+ case custom == "":
+ return source
+ case source == "":
+ return custom
+ }
+ return fmt.Sprintf("%s: %s", source, custom)
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go
new file mode 100644
index 0000000..6404ad3
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go
@@ -0,0 +1,62 @@
+package format_test
+
+import (
+ "testing"
+
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/internal/format"
+)
+
+func TestMessage(t *testing.T) {
+ var testcases = []struct {
+ doc string
+ args []interface{}
+ expected string
+ }{
+ {
+ doc: "none",
+ },
+ {
+ doc: "single string",
+ args: args("foo"),
+ expected: "foo",
+ },
+ {
+ doc: "single non-string",
+ args: args(123),
+ expected: "123",
+ },
+ {
+ doc: "format string and args",
+ args: args("%s %v", "a", 3),
+ expected: "a 3",
+ },
+ }
+
+ for _, tc := range testcases {
+ t.Run(tc.doc, func(t *testing.T) {
+ assert.Equal(t, format.Message(tc.args...), tc.expected)
+ })
+ }
+}
+
+func args(a ...interface{}) []interface{} {
+ return a
+}
+
+func TestWithCustomMessage(t *testing.T) {
+ t.Run("only custom", func(t *testing.T) {
+ msg := format.WithCustomMessage("", "extra")
+ assert.Equal(t, msg, "extra")
+ })
+
+ t.Run("only source", func(t *testing.T) {
+ msg := format.WithCustomMessage("source")
+ assert.Equal(t, msg, "source")
+ })
+
+ t.Run("source and custom", func(t *testing.T) {
+ msg := format.WithCustomMessage("source", "extra")
+ assert.Equal(t, msg, "source: extra")
+ })
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden
new file mode 100644
index 0000000..9518b0d
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden
@@ -0,0 +1,13 @@
+@@ -2,10 +2,8 @@
+ xyz
+ c
+-baba
++abab
+ z
+ t
+ j2j2
+ ok
+-ok
+-done
+-
++ok
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden
new file mode 100644
index 0000000..8202cbe
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden
@@ -0,0 +1,12 @@
+@@ -2,5 +2,5 @@
+ xyz
+ c
+-baba
++abab
+ z
+ t
+@@ -8,4 +8,3 @@
+ ok
+ ok
+-done
+
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden
new file mode 100644
index 0000000..6012d3f
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden
@@ -0,0 +1,7 @@
+--- from
++++ to
+@@ -1,3 +1,3 @@
+ a
+-xyz
++b
+ c
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden
new file mode 100644
index 0000000..de9a187
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden
@@ -0,0 +1,7 @@
+@@ -1,4 +1,4 @@
+ ··something
+-······something
+-····▽↵
++▷something
++··
+
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go
new file mode 100644
index 0000000..6d6a7cf
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go
@@ -0,0 +1,28 @@
+package maint // import "gotest.tools/v3/internal/maint"
+
+import (
+ "fmt"
+ "os"
+)
+
+// T provides an implementation of assert.TestingT which uses os.Exit, and
+// fmt.Println. This implementation can be used outside of test cases to provide
+// assert.TestingT, for example in a TestMain.
+var T = t{}
+
+type t struct{}
+
+// FailNow exits with a non-zero code
+func (t t) FailNow() {
+ os.Exit(1)
+}
+
+// Fail exits with a non-zero code
+func (t t) Fail() {
+ os.Exit(2)
+}
+
+// Log args by printing them to stdout
+func (t t) Log(args ...interface{}) {
+ fmt.Println(args...)
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go
new file mode 100644
index 0000000..392d9fe
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go
@@ -0,0 +1,52 @@
+package source
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+)
+
+func scanToDeferLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
+ var matchedNode ast.Node
+ ast.Inspect(node, func(node ast.Node) bool {
+ switch {
+ case node == nil || matchedNode != nil:
+ return false
+ case fileset.Position(node.End()).Line == lineNum:
+ if funcLit, ok := node.(*ast.FuncLit); ok {
+ matchedNode = funcLit
+ return false
+ }
+ }
+ return true
+ })
+ debug("defer line node: %s", debugFormatNode{matchedNode})
+ return matchedNode
+}
+
+func guessDefer(node ast.Node) (ast.Node, error) {
+ defers := collectDefers(node)
+ switch len(defers) {
+ case 0:
+ return nil, fmt.Errorf("failed to find expression in defer")
+ case 1:
+ return defers[0].Call, nil
+ default:
+ return nil, fmt.Errorf(
+ "ambiguous call expression: multiple (%d) defers in call block",
+ len(defers))
+ }
+}
+
+func collectDefers(node ast.Node) []*ast.DeferStmt {
+ var defers []*ast.DeferStmt
+ ast.Inspect(node, func(node ast.Node) bool {
+ if d, ok := node.(*ast.DeferStmt); ok {
+ defers = append(defers, d)
+ debug("defer: %s", debugFormatNode{d})
+ return false
+ }
+ return true
+ })
+ return defers
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go
new file mode 100644
index 0000000..a3f7008
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go
@@ -0,0 +1,147 @@
+package source // import "gotest.tools/v3/internal/source"
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "os"
+ "runtime"
+)
+
+// 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) {
+ args, err := CallExprArgs(stackIndex + 1)
+ if err != nil {
+ return "", err
+ }
+ if argPos >= len(args) {
+ return "", errors.New("failed to find expression")
+ }
+ return FormatNode(args[argPos])
+}
+
+// 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, line, ok := runtime.Caller(stackIndex + 1)
+ if !ok {
+ return nil, 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)
+ if err != nil {
+ 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
+ }
+ if node := scanToDeferLine(fileset, astFile, lineNum); node != nil {
+ node, err := guessDefer(node)
+ if err != nil || node != nil {
+ return node, err
+ }
+ }
+ return nil, nil
+}
+
+func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node {
+ var matchedNode ast.Node
+ ast.Inspect(node, func(node ast.Node) bool {
+ switch {
+ case node == nil || matchedNode != nil:
+ return false
+ case fileset.Position(node.Pos()).Line == lineNum:
+ matchedNode = node
+ return false
+ }
+ return true
+ })
+ return matchedNode
+}
+
+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")
+ }
+
+ debug("found node: %s", debugFormatNode{node})
+
+ visitor := &callExprVisitor{}
+ ast.Walk(visitor, node)
+ if visitor.expr == nil {
+ return nil, errors.New("failed to find call expression")
+ }
+ debug("callExpr: %s", debugFormatNode{visitor.expr})
+ return visitor.expr.Args, nil
+}
+
+type callExprVisitor struct {
+ expr *ast.CallExpr
+}
+
+func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor {
+ if v.expr != nil || node == nil {
+ return nil
+ }
+ debug("visit: %s", debugFormatNode{node})
+
+ switch typed := node.(type) {
+ case *ast.CallExpr:
+ v.expr = typed
+ return nil
+ case *ast.DeferStmt:
+ ast.Walk(v, typed.Call.Fun)
+ return nil
+ }
+ return v
+}
+
+// FormatNode using go/format.Node and return the result as a string
+func FormatNode(node ast.Node) (string, error) {
+ buf := new(bytes.Buffer)
+ err := format.Node(buf, token.NewFileSet(), node)
+ return buf.String(), err
+}
+
+var debugEnabled = os.Getenv("GOTESTTOOLS_DEBUG") != ""
+
+func debug(format string, args ...interface{}) {
+ if debugEnabled {
+ fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...)
+ }
+}
+
+type debugFormatNode struct {
+ ast.Node
+}
+
+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)
+ }
+ return fmt.Sprintf("(%T) %s", n.Node, out)
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go
new file mode 100644
index 0000000..3c21819
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go
@@ -0,0 +1,94 @@
+package source_test
+
+// using a separate package for test to avoid circular imports with the assert
+// package
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+ "testing"
+
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/internal/source"
+ "gotest.tools/v3/skip"
+)
+
+func TestFormattedCallExprArg_SingleLine(t *testing.T) {
+ msg, err := shim("not", "this", "this text")
+ assert.NilError(t, err)
+ assert.Equal(t, `"this text"`, msg)
+}
+
+func TestFormattedCallExprArg_MultiLine(t *testing.T) {
+ msg, err := shim(
+ "first",
+ "second",
+ "this text",
+ )
+ assert.NilError(t, err)
+ assert.Equal(t, `"this text"`, msg)
+}
+
+func TestFormattedCallExprArg_IfStatement(t *testing.T) {
+ if msg, err := shim(
+ "first",
+ "second",
+ "this text",
+ ); true {
+ assert.NilError(t, err)
+ assert.Equal(t, `"this text"`, msg)
+ }
+}
+
+func shim(_, _, _ string) (string, error) {
+ return source.FormattedCallExprArg(1, 2)
+}
+
+func TestFormattedCallExprArg_InDefer(t *testing.T) {
+ skip.If(t, isGoVersion18)
+ cap := &capture{}
+ func() {
+ defer cap.shim("first", "second")
+ }()
+
+ assert.NilError(t, cap.err)
+ assert.Equal(t, cap.value, `"second"`)
+}
+
+func isGoVersion18() bool {
+ return strings.HasPrefix(runtime.Version(), "go1.8.")
+}
+
+type capture struct {
+ value string
+ err error
+}
+
+func (c *capture) shim(_, _ string) {
+ c.value, c.err = source.FormattedCallExprArg(1, 1)
+}
+
+func TestFormattedCallExprArg_InAnonymousDefer(t *testing.T) {
+ cap := &capture{}
+ func() {
+ fmt.Println()
+ defer fmt.Println()
+ defer func() { cap.shim("first", "second") }()
+ }()
+
+ assert.NilError(t, cap.err)
+ assert.Equal(t, cap.value, `"second"`)
+}
+
+func TestFormattedCallExprArg_InDeferMultipleDefers(t *testing.T) {
+ skip.If(t, isGoVersion18)
+ cap := &capture{}
+ func() {
+ fmt.Println()
+ defer fmt.Println()
+ defer cap.shim("first", "second")
+ }()
+
+ assert.ErrorContains(t, cap.err, "ambiguous call expression")
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/update.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/update.go
new file mode 100644
index 0000000..bd9678b
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/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/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/version.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/version.go
new file mode 100644
index 0000000..5fa8a90
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/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/root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go
new file mode 100644
index 0000000..e7a858a
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go
@@ -0,0 +1,4 @@
+/*Package gotesttools is a collection of packages to augment `testing` and
+support common patterns.
+*/
+package gotesttools // import "gotest.tools/v3"
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go
new file mode 100644
index 0000000..46880f5
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go
@@ -0,0 +1,47 @@
+package poll
+
+import (
+ "net"
+ "os"
+)
+
+// Check is a function which will be used as check for the WaitOn method.
+type Check func(t LogT) Result
+
+// FileExists looks on filesystem and check that path exists.
+func FileExists(path string) Check {
+ return func(t LogT) Result {
+ if h, ok := t.(helperT); ok {
+ h.Helper()
+ }
+
+ _, err := os.Stat(path)
+ switch {
+ case os.IsNotExist(err):
+ t.Logf("waiting on file %s to exist", path)
+ return Continue("file %s does not exist", path)
+ case err != nil:
+ return Error(err)
+ default:
+ return Success()
+ }
+ }
+}
+
+// Connection try to open a connection to the address on the
+// named network. See net.Dial for a description of the network and
+// address parameters.
+func Connection(network, address string) Check {
+ return func(t LogT) Result {
+ if h, ok := t.(helperT); ok {
+ h.Helper()
+ }
+
+ _, err := net.Dial(network, address)
+ if err != nil {
+ t.Logf("waiting on socket %s://%s to be available...", network, address)
+ return Continue("socket %s://%s not available", network, address)
+ }
+ return Success()
+ }
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go
new file mode 100644
index 0000000..c853838
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go
@@ -0,0 +1,42 @@
+package poll
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "gotest.tools/v3/assert"
+)
+
+func TestWaitOnFile(t *testing.T) {
+ fakeFilePath := "./fakefile"
+
+ check := FileExists(fakeFilePath)
+
+ t.Run("file does not exist", func(t *testing.T) {
+ r := check(t)
+ assert.Assert(t, !r.Done())
+ assert.Equal(t, r.Message(), fmt.Sprintf("file %s does not exist", fakeFilePath))
+ })
+
+ os.Create(fakeFilePath)
+ defer os.Remove(fakeFilePath)
+
+ t.Run("file exists", func(t *testing.T) {
+ assert.Assert(t, check(t).Done())
+ })
+}
+
+func TestWaitOnSocketWithTimeout(t *testing.T) {
+ t.Run("connection to unavailable address", func(t *testing.T) {
+ check := Connection("tcp", "foo.bar:55555")
+ r := check(t)
+ assert.Assert(t, !r.Done())
+ assert.Equal(t, r.Message(), "socket tcp://foo.bar:55555 not available")
+ })
+
+ t.Run("connection to ", func(t *testing.T) {
+ check := Connection("tcp", "google.com:80")
+ assert.Assert(t, check(t).Done())
+ })
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go
new file mode 100644
index 0000000..2e3c32d
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go
@@ -0,0 +1,45 @@
+package poll_test
+
+import (
+ "fmt"
+ "time"
+
+ "gotest.tools/v3/poll"
+)
+
+var t poll.TestingT
+
+func numOfProcesses() (int, error) {
+ return 0, nil
+}
+
+func ExampleWaitOn() {
+ desired := 10
+
+ check := func(t poll.LogT) poll.Result {
+ actual, err := numOfProcesses()
+ if err != nil {
+ return poll.Error(fmt.Errorf("failed to get number of processes: %w", err))
+ }
+ if actual == desired {
+ return poll.Success()
+ }
+ t.Logf("waiting on process count to be %d...", desired)
+ return poll.Continue("number of processes is %d, not %d", actual, desired)
+ }
+
+ poll.WaitOn(t, check)
+}
+
+func isDesiredState() bool { return false }
+func getState() string { return "" }
+
+func ExampleSettingOp() {
+ check := func(t poll.LogT) poll.Result {
+ if isDesiredState() {
+ return poll.Success()
+ }
+ return poll.Continue("state is: %s", getState())
+ }
+ poll.WaitOn(t, check, poll.WithTimeout(30*time.Second), poll.WithDelay(15*time.Millisecond))
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go
new file mode 100644
index 0000000..29c5b40
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go
@@ -0,0 +1,171 @@
+/*Package poll provides tools for testing asynchronous code.
+ */
+package poll // import "gotest.tools/v3/poll"
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "gotest.tools/v3/assert/cmp"
+ "gotest.tools/v3/internal/assert"
+)
+
+// TestingT is the subset of testing.T used by WaitOn
+type TestingT interface {
+ LogT
+ Fatalf(format string, args ...interface{})
+}
+
+// LogT is a logging interface that is passed to the WaitOn check function
+type LogT interface {
+ Log(args ...interface{})
+ Logf(format string, args ...interface{})
+}
+
+type helperT interface {
+ Helper()
+}
+
+// Settings are used to configure the behaviour of WaitOn
+type Settings struct {
+ // Timeout is the maximum time to wait for the condition. Defaults to 10s.
+ Timeout time.Duration
+ // Delay is the time to sleep between checking the condition. Defaults to
+ // 100ms.
+ Delay time.Duration
+}
+
+func defaultConfig() *Settings {
+ return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond}
+}
+
+// SettingOp is a function which accepts and modifies Settings
+type SettingOp func(config *Settings)
+
+// WithDelay sets the delay to wait between polls
+func WithDelay(delay time.Duration) SettingOp {
+ return func(config *Settings) {
+ config.Delay = delay
+ }
+}
+
+// WithTimeout sets the timeout
+func WithTimeout(timeout time.Duration) SettingOp {
+ return func(config *Settings) {
+ config.Timeout = timeout
+ }
+}
+
+// Result of a check performed by WaitOn
+type Result interface {
+ // Error indicates that the check failed and polling should stop, and the
+ // the has failed
+ Error() error
+ // Done indicates that polling should stop, and the test should proceed
+ Done() bool
+ // Message provides the most recent state when polling has not completed
+ Message() string
+}
+
+type result struct {
+ done bool
+ message string
+ err error
+}
+
+func (r result) Done() bool {
+ return r.done
+}
+
+func (r result) Message() string {
+ return r.message
+}
+
+func (r result) Error() error {
+ return r.err
+}
+
+// Continue returns a Result that indicates to WaitOn that it should continue
+// polling. The message text will be used as the failure message if the timeout
+// is reached.
+func Continue(message string, args ...interface{}) Result {
+ return result{message: fmt.Sprintf(message, args...)}
+}
+
+// Success returns a Result where Done() returns true, which indicates to WaitOn
+// that it should stop polling and exit without an error.
+func Success() Result {
+ return result{done: true}
+}
+
+// Error returns a Result that indicates to WaitOn that it should fail the test
+// and stop polling.
+func Error(err error) Result {
+ return result{err: err}
+}
+
+// WaitOn a condition or until a timeout. Poll by calling check and exit when
+// check returns a done Result. To fail a test and exit polling with an error
+// return a error result.
+func WaitOn(t TestingT, check Check, pollOps ...SettingOp) {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ config := defaultConfig()
+ for _, pollOp := range pollOps {
+ pollOp(config)
+ }
+
+ var lastMessage string
+ after := time.After(config.Timeout)
+ chResult := make(chan Result)
+ for {
+ go func() {
+ chResult <- check(t)
+ }()
+ select {
+ case <-after:
+ if lastMessage == "" {
+ lastMessage = "first check never completed"
+ }
+ t.Fatalf("timeout hit after %s: %s", config.Timeout, lastMessage)
+ case result := <-chResult:
+ switch {
+ case result.Error() != nil:
+ t.Fatalf("polling check failed: %s", result.Error())
+ case result.Done():
+ return
+ }
+ time.Sleep(config.Delay)
+ lastMessage = result.Message()
+ }
+ }
+}
+
+// Compare values using the cmp.Comparison. If the comparison fails return a
+// result which indicates to WaitOn that it should continue waiting.
+// If the comparison is successful then WaitOn stops polling.
+func Compare(compare cmp.Comparison) Result {
+ buf := new(logBuffer)
+ if assert.RunComparison(buf, assert.ArgsAtZeroIndex, compare) {
+ return Success()
+ }
+ return Continue(buf.String())
+}
+
+type logBuffer struct {
+ log [][]interface{}
+}
+
+func (c *logBuffer) Log(args ...interface{}) {
+ c.log = append(c.log, args)
+}
+
+func (c *logBuffer) String() string {
+ b := new(strings.Builder)
+ for _, item := range c.log {
+ b.WriteString(fmt.Sprint(item...) + " ")
+ }
+ return b.String()
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go
new file mode 100644
index 0000000..36bd457
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go
@@ -0,0 +1,87 @@
+package poll
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/assert/cmp"
+)
+
+type fakeT struct {
+ failed string
+}
+
+func (t *fakeT) Fatalf(format string, args ...interface{}) {
+ t.failed = fmt.Sprintf(format, args...)
+ panic("exit wait on")
+}
+
+func (t *fakeT) Log(args ...interface{}) {}
+
+func (t *fakeT) Logf(format string, args ...interface{}) {}
+
+func TestWaitOn(t *testing.T) {
+ counter := 0
+ end := 4
+ check := func(t LogT) Result {
+ if counter == end {
+ return Success()
+ }
+ counter++
+ return Continue("counter is at %d not yet %d", counter-1, end)
+ }
+
+ WaitOn(t, check, WithDelay(0))
+ assert.Equal(t, end, counter)
+}
+
+func TestWaitOnWithTimeout(t *testing.T) {
+ fakeT := &fakeT{}
+
+ check := func(t LogT) Result {
+ return Continue("not done")
+ }
+
+ assert.Assert(t, cmp.Panics(func() {
+ WaitOn(fakeT, check, WithTimeout(time.Millisecond))
+ }))
+ assert.Equal(t, "timeout hit after 1ms: not done", fakeT.failed)
+}
+
+func TestWaitOnWithCheckTimeout(t *testing.T) {
+ fakeT := &fakeT{}
+
+ check := func(t LogT) Result {
+ time.Sleep(1 * time.Second)
+ return Continue("not done")
+ }
+
+ assert.Assert(t, cmp.Panics(func() { WaitOn(fakeT, check, WithTimeout(time.Millisecond)) }))
+ assert.Equal(t, "timeout hit after 1ms: first check never completed", fakeT.failed)
+}
+
+func TestWaitOnWithCheckError(t *testing.T) {
+ fakeT := &fakeT{}
+
+ check := func(t LogT) Result {
+ return Error(fmt.Errorf("broke"))
+ }
+
+ assert.Assert(t, cmp.Panics(func() { WaitOn(fakeT, check) }))
+ assert.Equal(t, "polling check failed: broke", fakeT.failed)
+}
+
+func TestWaitOn_WithCompare(t *testing.T) {
+ fakeT := &fakeT{}
+
+ check := func(t LogT) Result {
+ return Compare(cmp.Equal(3, 4))
+ }
+
+ assert.Assert(t, cmp.Panics(func() {
+ WaitOn(fakeT, check, WithDelay(0), WithTimeout(10*time.Millisecond))
+ }))
+ assert.Assert(t, cmp.Contains(fakeT.failed, "assertion failed: 3 (int) != 4 (int)"))
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify
new file mode 100644
index 0000000..cb79fdd
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+exec go build -o ./dist/gty-migrate-from-testify ./assert/cmd/gty-migrate-from-testify
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit
new file mode 100644
index 0000000..0ed48f0
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -eu -o pipefail
+
+paths=${@:-$(go list ./... | grep -v '/vendor/')}
+TESTFLAGS=${TESTFLAGS-}
+TESTTIMEOUT=${TESTTIMEOUT-30s}
+go test -test.timeout "$TESTTIMEOUT" $TESTFLAGS -v $paths
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go
new file mode 100644
index 0000000..31c2148
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go
@@ -0,0 +1,43 @@
+package skip_test
+
+import (
+ "testing"
+
+ "gotest.tools/v3/skip"
+)
+
+var apiVersion = ""
+
+type env struct{}
+
+func (e env) hasFeature(_ string) bool { return false }
+
+var testEnv = env{}
+
+func MissingFeature() bool { return false }
+
+var t = &testing.T{}
+
+func ExampleIf() {
+ // --- SKIP: TestName (0.00s)
+ // skip.go:19: MissingFeature
+ skip.If(t, MissingFeature)
+
+ // --- SKIP: TestName (0.00s)
+ // skip.go:19: MissingFeature: coming soon
+ skip.If(t, MissingFeature, "coming soon")
+}
+
+func ExampleIf_withExpression() {
+ // --- SKIP: TestName (0.00s)
+ // skip.go:19: apiVersion < version("v1.24")
+ skip.If(t, apiVersion < version("v1.24"))
+
+ // --- SKIP: TestName (0.00s)
+ // skip.go:19: !textenv.hasFeature("build"): coming soon
+ skip.If(t, !testEnv.hasFeature("build"), "coming soon")
+}
+
+func version(v string) string {
+ return v
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go
new file mode 100644
index 0000000..cb899f7
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go
@@ -0,0 +1,89 @@
+/*Package skip provides functions for skipping a test and printing the source code
+of the condition used to skip the test.
+*/
+package skip // import "gotest.tools/v3/skip"
+
+import (
+ "fmt"
+ "path"
+ "reflect"
+ "runtime"
+ "strings"
+
+ "gotest.tools/v3/internal/format"
+ "gotest.tools/v3/internal/source"
+)
+
+type skipT interface {
+ Skip(args ...interface{})
+ Log(args ...interface{})
+}
+
+// Result of skip function
+type Result interface {
+ Skip() bool
+ Message() string
+}
+
+type helperT interface {
+ Helper()
+}
+
+// BoolOrCheckFunc can be a bool, func() bool, or func() Result. Other types will panic
+type BoolOrCheckFunc interface{}
+
+// If the condition expression evaluates to true, skip the test.
+//
+// The condition argument may be one of three types: bool, func() bool, or
+// func() SkipResult.
+// When called with a bool, the test will be skip if the condition evaluates to true.
+// When called with a func() bool, the test will be skip if the function returns true.
+// When called with a func() Result, the test will be skip if the Skip method
+// of the result returns true.
+// The skip message will contain the source code of the expression.
+// Extra message text can be passed as a format string with args.
+func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ switch check := condition.(type) {
+ case bool:
+ ifCondition(t, check, msgAndArgs...)
+ case func() bool:
+ if check() {
+ t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...))
+ }
+ case func() Result:
+ result := check()
+ if result.Skip() {
+ msg := getFunctionName(check) + ": " + result.Message()
+ t.Skip(format.WithCustomMessage(msg, msgAndArgs...))
+ }
+ default:
+ panic(fmt.Sprintf("invalid type for condition arg: %T", check))
+ }
+}
+
+func getFunctionName(function interface{}) string {
+ funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
+ return strings.SplitN(path.Base(funcPath), ".", 2)[1]
+}
+
+func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
+ if ht, ok := t.(helperT); ok {
+ ht.Helper()
+ }
+ if !condition {
+ return
+ }
+ const (
+ stackIndex = 2
+ argPos = 1
+ )
+ source, err := source.FormattedCallExprArg(stackIndex, argPos)
+ if err != nil {
+ t.Log(err.Error())
+ t.Skip(format.Message(msgAndArgs...))
+ }
+ t.Skip(format.WithCustomMessage(source, msgAndArgs...))
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go
new file mode 100644
index 0000000..49b4dda
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go
@@ -0,0 +1,136 @@
+package skip
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/assert/cmp"
+)
+
+type fakeSkipT struct {
+ reason string
+ logs []string
+}
+
+func (f *fakeSkipT) Skip(args ...interface{}) {
+ buf := new(bytes.Buffer)
+ for _, arg := range args {
+ buf.WriteString(fmt.Sprintf("%s", arg))
+ }
+ f.reason = buf.String()
+}
+
+func (f *fakeSkipT) Log(args ...interface{}) {
+ f.logs = append(f.logs, fmt.Sprintf("%s", args[0]))
+}
+
+func (f *fakeSkipT) Helper() {}
+
+func version(v string) string {
+ return v
+}
+
+func TestIfCondition(t *testing.T) {
+ skipT := &fakeSkipT{}
+ apiVersion := "v1.4"
+ If(skipT, apiVersion < version("v1.6"))
+
+ assert.Equal(t, `apiVersion < version("v1.6")`, skipT.reason)
+ assert.Assert(t, cmp.Len(skipT.logs, 0))
+}
+
+func TestIfConditionWithMessage(t *testing.T) {
+ skipT := &fakeSkipT{}
+ apiVersion := "v1.4"
+ If(skipT, apiVersion < "v1.6", "see notes")
+
+ assert.Equal(t, `apiVersion < "v1.6": see notes`, skipT.reason)
+ assert.Assert(t, cmp.Len(skipT.logs, 0))
+}
+
+func TestIfConditionMultiline(t *testing.T) {
+ skipT := &fakeSkipT{}
+ apiVersion := "v1.4"
+ If(
+ skipT,
+ apiVersion < "v1.6")
+
+ assert.Equal(t, `apiVersion < "v1.6"`, skipT.reason)
+ assert.Assert(t, cmp.Len(skipT.logs, 0))
+}
+
+func TestIfConditionMultilineWithMessage(t *testing.T) {
+ skipT := &fakeSkipT{}
+ apiVersion := "v1.4"
+ If(
+ skipT,
+ apiVersion < "v1.6",
+ "see notes")
+
+ assert.Equal(t, `apiVersion < "v1.6": see notes`, skipT.reason)
+ assert.Assert(t, cmp.Len(skipT.logs, 0))
+}
+
+func TestIfConditionNoSkip(t *testing.T) {
+ skipT := &fakeSkipT{}
+ If(skipT, false)
+
+ assert.Equal(t, "", skipT.reason)
+ assert.Assert(t, cmp.Len(skipT.logs, 0))
+}
+
+func SkipBecauseISaidSo() bool {
+ return true
+}
+
+func TestIf(t *testing.T) {
+ skipT := &fakeSkipT{}
+ If(skipT, SkipBecauseISaidSo)
+
+ assert.Equal(t, "SkipBecauseISaidSo", skipT.reason)
+}
+
+func TestIfWithMessage(t *testing.T) {
+ skipT := &fakeSkipT{}
+ If(skipT, SkipBecauseISaidSo, "see notes")
+
+ assert.Equal(t, "SkipBecauseISaidSo: see notes", skipT.reason)
+}
+
+func TestIf_InvalidCondition(t *testing.T) {
+ skipT := &fakeSkipT{}
+ assert.Assert(t, cmp.Panics(func() {
+ If(skipT, "just a string")
+ }))
+}
+
+func TestIfWithSkipResultFunc(t *testing.T) {
+ t.Run("no extra message", func(t *testing.T) {
+ skipT := &fakeSkipT{}
+ If(skipT, alwaysSkipWithMessage)
+
+ assert.Equal(t, "alwaysSkipWithMessage: skip because I said so!", skipT.reason)
+ })
+ t.Run("with extra message", func(t *testing.T) {
+ skipT := &fakeSkipT{}
+ If(skipT, alwaysSkipWithMessage, "also %v", 4)
+
+ assert.Equal(t, "alwaysSkipWithMessage: skip because I said so!: also 4", skipT.reason)
+ })
+}
+
+func alwaysSkipWithMessage() Result {
+ return skipResult{}
+}
+
+type skipResult struct{}
+
+func (s skipResult) Skip() bool {
+ return true
+}
+
+func (s skipResult) Message() string {
+ return "skip because I said so!"
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go
new file mode 100644
index 0000000..90f4541
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go
@@ -0,0 +1,5 @@
+/*Package x is a namespace for experimental packages. Packages under x have looser
+compatibility requirements. Packages in this namespace may contain backwards
+incompatible changes within the same major version.
+*/
+package x // import "gotest.tools/v3/x"
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go
new file mode 100644
index 0000000..708d940
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go
@@ -0,0 +1,93 @@
+/*Package subtest provides a TestContext to subtests which handles cleanup, and
+provides a testing.TB, and context.Context.
+
+This package was inspired by github.com/frankban/quicktest.
+
+DEPRECATED
+
+With the addition of T.Cleanup() in go1.14 this package provides very
+little value. A context.Context can be managed by tests that need it with
+little enough boilerplate that it doesn't make sense to wrap testing.T in a
+TestContext.
+*/
+package subtest // import "gotest.tools/v3/x/subtest"
+
+import (
+ "context"
+ "testing"
+
+ "gotest.tools/v3/internal/cleanup"
+)
+
+type testcase struct {
+ testing.TB
+ ctx context.Context
+ cleanupFuncs []cleanupFunc
+}
+
+type cleanupFunc func()
+
+func (tc *testcase) Ctx() context.Context {
+ if tc.ctx == nil {
+ var cancel func()
+ tc.ctx, cancel = context.WithCancel(context.Background())
+ cleanup.Cleanup(tc, cancel)
+ }
+ return tc.ctx
+}
+
+// cleanup runs all cleanup functions. Functions are run in the opposite order
+// in which they were added. Cleanup is called automatically before Run exits.
+func (tc *testcase) cleanup() {
+ for _, f := range tc.cleanupFuncs {
+ // Defer all cleanup functions so they all run even if one calls
+ // t.FailNow() or panics. Deferring them also runs them in reverse order.
+ defer f()
+ }
+ tc.cleanupFuncs = nil
+}
+
+func (tc *testcase) AddCleanup(f func()) {
+ tc.cleanupFuncs = append(tc.cleanupFuncs, f)
+}
+
+func (tc *testcase) Parallel() {
+ tp, ok := tc.TB.(parallel)
+ if !ok {
+ panic("Parallel called with a testing.B")
+ }
+ tp.Parallel()
+}
+
+type parallel interface {
+ Parallel()
+}
+
+// Run a subtest. When subtest exits, every cleanup function added with
+// TestContext.AddCleanup will be run.
+func Run(t *testing.T, name string, subtest func(t TestContext)) bool {
+ return t.Run(name, func(t *testing.T) {
+ tc := &testcase{TB: t}
+ defer tc.cleanup()
+ subtest(tc)
+ })
+}
+
+// TestContext provides a testing.TB and a context.Context for a test case.
+type TestContext interface {
+ testing.TB
+ // AddCleanup function which will be run when before Run returns.
+ //
+ // Deprecated: Go 1.14+ now includes a testing.TB.Cleanup(func()) which
+ // should be used instead. AddCleanup will be removed in a future release.
+ AddCleanup(f func())
+ // Ctx returns a context for the test case. Multiple calls from the same subtest
+ // will return the same context. The context is cancelled when Run
+ // returns.
+ Ctx() context.Context
+ // Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does
+ // not implement Parallel.
+ Parallel()
+}
+
+var _ TestContext = &testcase{}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go
new file mode 100644
index 0000000..56b316e
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go
@@ -0,0 +1,32 @@
+package subtest
+
+import (
+ "context"
+ "testing"
+
+ "gotest.tools/v3/assert"
+)
+
+func TestTestcase_Run_CallsCleanup(t *testing.T) {
+ calls := []int{}
+ var ctx context.Context
+ Run(t, "test-run-cleanup", func(t TestContext) {
+ cleanup := func(n int) func() {
+ return func() {
+ calls = append(calls, n)
+ }
+ }
+ ctx = t.Ctx()
+ t.AddCleanup(cleanup(2))
+ t.AddCleanup(cleanup(1))
+ t.AddCleanup(cleanup(0))
+ })
+ assert.DeepEqual(t, calls, []int{0, 1, 2})
+ assert.Equal(t, ctx.Err(), context.Canceled)
+}
+
+func TestTestcase_Run_Parallel(t *testing.T) {
+ Run(t, "test-parallel", func(t TestContext) {
+ t.Parallel()
+ })
+}
diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go
new file mode 100644
index 0000000..b4365e0
--- /dev/null
+++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go
@@ -0,0 +1,66 @@
+package subtest_test
+
+import (
+ "io"
+ "net/http"
+ "strings"
+ "testing"
+
+ "gotest.tools/v3/assert"
+ "gotest.tools/v3/x/subtest"
+)
+
+var t = &testing.T{}
+
+func ExampleRun_tableTest() {
+ var testcases = []struct {
+ data io.Reader
+ expected int
+ }{
+ {
+ data: strings.NewReader("invalid input"),
+ expected: 400,
+ },
+ {
+ data: strings.NewReader("valid input"),
+ expected: 200,
+ },
+ }
+
+ for _, tc := range testcases {
+ subtest.Run(t, "test-service-call", func(t subtest.TestContext) {
+ // startFakeService can shutdown using t.AddCleanup
+ url := startFakeService(t)
+
+ req, err := http.NewRequest("POST", url, tc.data)
+ assert.NilError(t, err)
+ req = req.WithContext(t.Ctx())
+
+ client := newClient(t)
+ resp, err := client.Do(req)
+ assert.NilError(t, err)
+ assert.Equal(t, resp.StatusCode, tc.expected)
+ })
+ }
+}
+
+func startFakeService(t subtest.TestContext) string {
+ return "url"
+}
+
+func newClient(_ subtest.TestContext) *http.Client {
+ return &http.Client{}
+}
+
+func ExampleRun_testSuite() {
+ // do suite setup before subtests
+
+ subtest.Run(t, "test-one", func(t subtest.TestContext) {
+ assert.Equal(t, 1, 1)
+ })
+ subtest.Run(t, "test-two", func(t subtest.TestContext) {
+ assert.Equal(t, 2, 2)
+ })
+
+ // do suite teardown after subtests
+}
diff --git a/root/pkg/sumdb/sum.golang.org/latest b/root/pkg/sumdb/sum.golang.org/latest
new file mode 100644
index 0000000..829c8df
--- /dev/null
+++ b/root/pkg/sumdb/sum.golang.org/latest
@@ -0,0 +1,5 @@
+go.sum database tree
+46687924
+oqyj00KFhQ1r/LMLYjS+N2ZcyMBmuy4E20Qx5wubH18=
+
+— sum.golang.org Az3grskfHYkRAlx6xDdD0Ld7PWz6CLORxgtPBxvd73HO8lvyC+Z2yYTTobhK5XRat+vJH6yqO8Oqxa4++MKlzqCl/QY=
diff --git a/root/src/github.com/getyoti/age-scan-examples/go.sum b/root/src/github.com/getyoti/age-scan-examples/go.sum
new file mode 100644
index 0000000..e553463
--- /dev/null
+++ b/root/src/github.com/getyoti/age-scan-examples/go.sum
@@ -0,0 +1,8 @@
+github.com/getyoti/yoti-go-sdk/v3 v3.14.0 h1:cMFC/PuN6kuxMfPwX4OkVyQDA5ZousWnVMfUhIhKZa0=
+github.com/getyoti/yoti-go-sdk/v3 v3.14.0/go.mod h1:FH8g7mRttc6SBUd9P0Jihm7ut0rNhkU3rDFljUHL33I=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
+gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=