From dd9478a1f7920d6c48789f54644336e1bfe609d4 Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Fri, 8 Jun 2018 18:23:38 +0200 Subject: [PATCH] Replace gotestyourself by gotest.tools github.com/gotestyourself/gotestyourself moved to gotest.tools with version 2.0.0. Moving to that one, bumping it to v2.1.0. Signed-off-by: Vincent Demeester --- vendor.conf | 3 +- .../gotestyourself/gotestyourself/README.md | 33 -- .../github.com/pmezard/go-difflib/README.md | 50 --- .../gotestyourself => gotest.tools}/LICENSE | 0 vendor/gotest.tools/README.md | 31 ++ .../assert/assert.go | 46 ++- .../assert/cmp/compare.go | 36 +- .../assert/cmp/result.go | 2 +- .../assert/result.go | 6 +- .../env/env.go | 23 +- .../fs/file.go | 19 +- vendor/gotest.tools/fs/manifest.go | 129 +++++++ vendor/gotest.tools/fs/manifest_unix.go | 30 ++ vendor/gotest.tools/fs/manifest_windows.go | 22 ++ .../gotestyourself => gotest.tools}/fs/ops.go | 89 ++++- vendor/gotest.tools/fs/path.go | 151 ++++++++ vendor/gotest.tools/fs/report.go | 215 +++++++++++ .../golden/golden.go | 40 +- .../icmd/command.go | 7 +- .../icmd/exitcode.go | 0 .../icmd/ops.go | 0 .../internal/difflib}/LICENSE | 0 .../internal}/difflib/difflib.go | 364 +----------------- vendor/gotest.tools/internal/format/diff.go | 161 ++++++++ .../internal/format/format.go | 2 +- .../internal/source/source.go | 2 +- .../poll/poll.go | 10 +- .../skip/skip.go | 30 +- vendor/gotest.tools/x/subtest/context.go | 81 ++++ 29 files changed, 1034 insertions(+), 548 deletions(-) delete mode 100644 vendor/github.com/gotestyourself/gotestyourself/README.md delete mode 100644 vendor/github.com/pmezard/go-difflib/README.md rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/LICENSE (100%) create mode 100644 vendor/gotest.tools/README.md rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/assert/assert.go (85%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/assert/cmp/compare.go (90%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/assert/cmp/result.go (97%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/assert/result.go (93%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/env/env.go (84%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/fs/file.go (77%) create mode 100644 vendor/gotest.tools/fs/manifest.go create mode 100644 vendor/gotest.tools/fs/manifest_unix.go create mode 100644 vendor/gotest.tools/fs/manifest_windows.go rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/fs/ops.go (61%) create mode 100644 vendor/gotest.tools/fs/path.go create mode 100644 vendor/gotest.tools/fs/report.go rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/golden/golden.go (76%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/icmd/command.go (98%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/icmd/exitcode.go (100%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/icmd/ops.go (100%) rename vendor/{github.com/pmezard/go-difflib => gotest.tools/internal/difflib}/LICENSE (100%) rename vendor/{github.com/pmezard/go-difflib => gotest.tools/internal}/difflib/difflib.go (56%) create mode 100644 vendor/gotest.tools/internal/format/diff.go rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/internal/format/format.go (91%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/internal/source/source.go (98%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/poll/poll.go (95%) rename vendor/{github.com/gotestyourself/gotestyourself => gotest.tools}/skip/skip.go (60%) create mode 100644 vendor/gotest.tools/x/subtest/context.go diff --git a/vendor.conf b/vendor.conf index 0c44656e5e..48768622e3 100755 --- a/vendor.conf +++ b/vendor.conf @@ -29,7 +29,7 @@ github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c github.com/googleapis/gnostic e4f56557df6250e1945ee6854f181ce4e1c2c646 github.com/gorilla/context v1.1 github.com/gorilla/mux v1.1 -github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d02909 +gotest.tools v2.1.0 github.com/go-openapi/jsonpointer 46af16f9f7b149af66e5d1bd010e3574dc06de98 github.com/go-openapi/jsonreference 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 github.com/go-openapi/spec 6aced65f8501fe1217321abf0749d354824ba2ff @@ -56,7 +56,6 @@ github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/runc 4fc53a81fb7c994640722ac585fa9ca548971871 github.com/peterbourgon/diskv 5f041e8faa004a95c88a202771f4cc3e991971e6 github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 -github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8 diff --git a/vendor/github.com/gotestyourself/gotestyourself/README.md b/vendor/github.com/gotestyourself/gotestyourself/README.md deleted file mode 100644 index aad893ae03..0000000000 --- a/vendor/github.com/gotestyourself/gotestyourself/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Go Test Yourself - -A collection of packages compatible with `go test` to support common testing -patterns. - -[![GoDoc](https://godoc.org/github.com/gotestyourself/gotestyourself?status.svg)](https://godoc.org/github.com/gotestyourself/gotestyourself) -[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master) -[![Go Reportcard](https://goreportcard.com/badge/github.com/gotestyourself/gotestyourself)](https://goreportcard.com/report/github.com/gotestyourself/gotestyourself) - - -## Packages - -* [assert](http://godoc.org/github.com/gotestyourself/gotestyourself/assert) - - compare values and fail the test when the comparison fails -* [env](http://godoc.org/github.com/gotestyourself/gotestyourself/env) - - test code that uses environment variables -* [fs](http://godoc.org/github.com/gotestyourself/gotestyourself/fs) - - create test files and directories -* [golden](http://godoc.org/github.com/gotestyourself/gotestyourself/golden) - - compare large multi-line strings -* [icmd](http://godoc.org/github.com/gotestyourself/gotestyourself/icmd) - - execute binaries and test the output -* [poll](http://godoc.org/github.com/gotestyourself/gotestyourself/poll) - - test asynchronous code by polling until a desired state is reached -* [skip](http://godoc.org/github.com/gotestyourself/gotestyourself/skip) - - skip tests based on conditions -* [testsum](http://godoc.org/github.com/gotestyourself/gotestyourself/testsum) - - a program to summarize `go test` output and test failures - -## Related - -* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces -* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time` diff --git a/vendor/github.com/pmezard/go-difflib/README.md b/vendor/github.com/pmezard/go-difflib/README.md deleted file mode 100644 index e87f307ed4..0000000000 --- a/vendor/github.com/pmezard/go-difflib/README.md +++ /dev/null @@ -1,50 +0,0 @@ -go-difflib -========== - -[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib) -[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib) - -Go-difflib is a partial port of python 3 difflib package. Its main goal -was to make unified and context diff available in pure Go, mostly for -testing purposes. - -The following class and functions (and related tests) have be ported: - -* `SequenceMatcher` -* `unified_diff()` -* `context_diff()` - -## Installation - -```bash -$ go get github.com/pmezard/go-difflib/difflib -``` - -### Quick Start - -Diffs are configured with Unified (or ContextDiff) structures, and can -be output to an io.Writer or returned as a string. - -```Go -diff := UnifiedDiff{ - A: difflib.SplitLines("foo\nbar\n"), - B: difflib.SplitLines("foo\nbaz\n"), - FromFile: "Original", - ToFile: "Current", - Context: 3, -} -text, _ := GetUnifiedDiffString(diff) -fmt.Printf(text) -``` - -would output: - -``` ---- Original -+++ Current -@@ -1,3 +1,3 @@ - foo --bar -+baz -``` - diff --git a/vendor/github.com/gotestyourself/gotestyourself/LICENSE b/vendor/gotest.tools/LICENSE similarity index 100% rename from vendor/github.com/gotestyourself/gotestyourself/LICENSE rename to vendor/gotest.tools/LICENSE diff --git a/vendor/gotest.tools/README.md b/vendor/gotest.tools/README.md new file mode 100644 index 0000000000..f5df204315 --- /dev/null +++ b/vendor/gotest.tools/README.md @@ -0,0 +1,31 @@ +# gotest.tools + +A collection of packages to augment `testing` and support common patterns. + +[![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://godoc.org/gotest.tools) +[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master) +[![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools) + + +## Packages + +* [assert](http://godoc.org/gotest.tools/assert) - + compare values and fail the test when a comparison fails +* [env](http://godoc.org/gotest.tools/env) - + test code which uses environment variables +* [fs](http://godoc.org/gotest.tools/fs) - + create temporary files and compare a filesystem tree to an expected value +* [golden](http://godoc.org/gotest.tools/golden) - + compare large multi-line strings against values frozen in golden files +* [icmd](http://godoc.org/gotest.tools/icmd) - + execute binaries and test the output +* [poll](http://godoc.org/gotest.tools/poll) - + test asynchronous code by polling until a desired state is reached +* [skip](http://godoc.org/gotest.tools/skip) - + skip a test and print the source code of the condition used to skip the test + +## Related + +* [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - go test runner with custom output +* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces +* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time` diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go b/vendor/gotest.tools/assert/assert.go similarity index 85% rename from vendor/github.com/gotestyourself/gotestyourself/assert/assert.go rename to vendor/gotest.tools/assert/assert.go index d2b05f4165..05d6635436 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go +++ b/vendor/gotest.tools/assert/assert.go @@ -8,7 +8,7 @@ comparison fails. The one difference is that Assert() will end the test executio immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()), return the value of the comparison, then proceed with the rest of the test case. -Example Usage +Example usage The example below shows assert used with some common types. @@ -16,8 +16,8 @@ The example below shows assert used with some common types. import ( "testing" - "github.com/gotestyourself/gotestyourself/assert" - is "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestEverything(t *testing.T) { @@ -49,12 +49,20 @@ The example below shows assert used with some common types. Comparisons -https://godoc.org/github.com/gotestyourself/gotestyourself/assert/cmp provides +Package https://godoc.org/gotest.tools/assert/cmp provides many common comparisons. Additional comparisons can be written to compare values in other ways. See the example Assert (CustomComparison). +Automated migration from testify + +gty-migrate-from-testify is a binary which can update source code which uses +testify assertions to use the assertions provided by this package. + +See http://bit.do/cmd-gty-migrate-from-testify. + + */ -package assert +package assert // import "gotest.tools/assert" import ( "fmt" @@ -62,9 +70,9 @@ import ( "go/token" gocmp "github.com/google/go-cmp/cmp" - "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/internal/format" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" + "gotest.tools/internal/source" ) // BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage. @@ -234,7 +242,17 @@ func NilError(t TestingT, err error, msgAndArgs ...interface{}) { } // Equal uses the == operator to assert two values are equal and fails the test -// if they are not equal. This is equivalent to Assert(t, cmp.Equal(x, y)). +// if they are not equal. +// +// If the comparison fails Equal will use the variable names for x and y as part +// of the failure message to identify the actual and expected values. +// +// If either x or y are a multi-line string the failure message will include a +// unified diff of the two values. If the values only differ by whitespace +// the unified diff will be augmented by replacing whitespace characters with +// visible characters to identify the whitespace difference. +// +// This is equivalent to Assert(t, cmp.Equal(x, y)). func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() @@ -242,8 +260,12 @@ func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...) } -// DeepEqual uses https://github.com/google/go-cmp/cmp to assert two values -// are equal and fails the test if they are not equal. +// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are +// equal and fails the test if they are not equal. +// +// Package https://godoc.org/gotest.tools/assert/opt provides some additional +// commonly used Options. +// // This is equivalent to Assert(t, cmp.DeepEqual(x, y)). func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { if ht, ok := t.(helperT); ok { @@ -276,7 +298,7 @@ func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interf // // Expected can be one of: // a func(error) bool which returns true if the error is the expected type, -// an instance of a struct of the expected type, +// an instance of (or a pointer to) a struct of the expected type, // a pointer to an interface the error is expected to implement, // a reflect.Type of the expected struct or interface. // diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go b/vendor/gotest.tools/assert/cmp/compare.go similarity index 90% rename from vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go rename to vendor/gotest.tools/assert/cmp/compare.go index 64a25f00b3..ae03749e20 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go +++ b/vendor/gotest.tools/assert/cmp/compare.go @@ -1,5 +1,5 @@ /*Package cmp provides Comparisons for Assert and Check*/ -package cmp +package cmp // import "gotest.tools/assert/cmp" import ( "fmt" @@ -7,7 +7,7 @@ import ( "strings" "github.com/google/go-cmp/cmp" - "github.com/pmezard/go-difflib/difflib" + "gotest.tools/internal/format" ) // Comparison is a function which compares values and returns ResultSuccess if @@ -15,10 +15,12 @@ import ( // Result will contain a message about why it failed. type Comparison func() Result -// DeepEqual compares two values using https://godoc.org/github.com/google/go-cmp/cmp +// DeepEqual compares two values using google/go-cmp (http://bit.do/go-cmp) // and succeeds if the values are equal. // // The comparison can be customized using comparison Options. +// Package https://godoc.org/gotest.tools/assert/opt provides some additional +// commonly used Options. func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { return func() (result Result) { defer func() { @@ -27,7 +29,10 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { } }() diff := cmp.Diff(x, y, opts...) - return toResult(diff == "", "\n"+diff) + if diff == "" { + return ResultSuccess + } + return multiLineDiffResult(diff) } } @@ -53,14 +58,15 @@ func toResult(success bool, msg string) Result { return ResultFailure(msg) } -// Equal succeeds if x == y. +// Equal succeeds if x == y. See assert.Equal for full documentation. func Equal(x, y interface{}) Comparison { return func() Result { switch { case x == y: return ResultSuccess case isMultiLineStringCompare(x, y): - return multiLineStringDiffResult(x.(string), y.(string)) + diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)}) + return multiLineDiffResult(diff) } return ResultFailureTemplate(` {{- .Data.x}} ( @@ -86,15 +92,7 @@ func isMultiLineStringCompare(x, y interface{}) bool { return strings.Contains(strX, "\n") || strings.Contains(strY, "\n") } -func multiLineStringDiffResult(x, y string) Result { - diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(x), - B: difflib.SplitLines(y), - Context: 3, - }) - if err != nil { - return ResultFailure(fmt.Sprintf("failed to diff: %s", err)) - } +func multiLineDiffResult(diff string) Result { return ResultFailureTemplate(` --- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}} +++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}} @@ -242,7 +240,7 @@ func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison { // // Expected can be one of: // a func(error) bool which returns true if the error is the expected type, -// an instance of a struct of the expected type, +// an instance of (or a pointer to) a struct of the expected type, // a pointer to an interface the error is expected to implement, // a reflect.Type of the expected struct or interface. func ErrorType(err error, expected interface{}) Comparison { @@ -261,7 +259,7 @@ func ErrorType(err error, expected interface{}) Comparison { expectedType := reflect.TypeOf(expected) switch { - case expectedType.Kind() == reflect.Struct: + case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType): return cmpErrorTypeEqualType(err, expectedType) case isPtrToInterface(expectedType): return cmpErrorTypeImplementsType(err, expectedType.Elem()) @@ -308,3 +306,7 @@ func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result { func isPtrToInterface(typ reflect.Type) bool { return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface } + +func isPtrToStruct(typ reflect.Type) bool { + return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go b/vendor/gotest.tools/assert/cmp/result.go similarity index 97% rename from vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go rename to vendor/gotest.tools/assert/cmp/result.go index 99f39eeb27..7c3c37dd72 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go +++ b/vendor/gotest.tools/assert/cmp/result.go @@ -6,7 +6,7 @@ import ( "go/ast" "text/template" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/internal/source" ) // Result of a Comparison. diff --git a/vendor/github.com/gotestyourself/gotestyourself/assert/result.go b/vendor/gotest.tools/assert/result.go similarity index 93% rename from vendor/github.com/gotestyourself/gotestyourself/assert/result.go rename to vendor/gotest.tools/assert/result.go index b685b7f3e5..3900264d0b 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/assert/result.go +++ b/vendor/gotest.tools/assert/result.go @@ -4,9 +4,9 @@ import ( "fmt" "go/ast" - "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/gotestyourself/gotestyourself/internal/format" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" + "gotest.tools/internal/source" ) func runComparison( diff --git a/vendor/github.com/gotestyourself/gotestyourself/env/env.go b/vendor/gotest.tools/env/env.go similarity index 84% rename from vendor/github.com/gotestyourself/gotestyourself/env/env.go rename to vendor/gotest.tools/env/env.go index 700430f1dd..ed0f189916 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/env/env.go +++ b/vendor/gotest.tools/env/env.go @@ -1,13 +1,14 @@ /*Package env provides functions to test code that read environment variables or the current working directory. */ -package env +package env // import "gotest.tools/env" import ( "os" "strings" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" + "gotest.tools/x/subtest" ) type helperT interface { @@ -23,7 +24,7 @@ func Patch(t assert.TestingT, key, value string) func() { } oldValue, ok := os.LookupEnv(key) assert.NilError(t, os.Setenv(key, value)) - return func() { + cleanup := func() { if ht, ok := t.(helperT); ok { ht.Helper() } @@ -33,6 +34,10 @@ func Patch(t assert.TestingT, key, value string) func() { } assert.NilError(t, os.Setenv(key, oldValue)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(cleanup) + } + return cleanup } // PatchAll sets the environment to env, and returns a function which will @@ -47,7 +52,7 @@ func PatchAll(t assert.TestingT, env map[string]string) func() { for key, value := range env { assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value) } - return func() { + cleanup := func() { if ht, ok := t.(helperT); ok { ht.Helper() } @@ -56,6 +61,10 @@ func PatchAll(t assert.TestingT, env map[string]string) func() { assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal) } } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(cleanup) + } + return cleanup } // ToMap takes a list of strings in the format returned by os.Environ() and @@ -89,10 +98,14 @@ func ChangeWorkingDir(t assert.TestingT, dir string) func() { cwd, err := os.Getwd() assert.NilError(t, err) assert.NilError(t, os.Chdir(dir)) - return func() { + cleanup := func() { if ht, ok := t.(helperT); ok { ht.Helper() } assert.NilError(t, os.Chdir(cwd)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(cleanup) + } + return cleanup } diff --git a/vendor/github.com/gotestyourself/gotestyourself/fs/file.go b/vendor/gotest.tools/fs/file.go similarity index 77% rename from vendor/github.com/gotestyourself/gotestyourself/fs/file.go rename to vendor/gotest.tools/fs/file.go index a2f731093b..8bf0188d2f 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/fs/file.go +++ b/vendor/gotest.tools/fs/file.go @@ -1,17 +1,20 @@ -/*Package fs provides tools for creating and working with temporary files and -directories. +/*Package fs provides tools for creating temporary files, and testing the +contents and structure of a directory. */ -package fs +package fs // import "gotest.tools/fs" import ( "io/ioutil" "os" "path/filepath" - "github.com/gotestyourself/gotestyourself/assert" + "gotest.tools/assert" + "gotest.tools/x/subtest" ) -// Path objects return their filesystem path. Both File and Dir implement Path. +// Path objects return their filesystem path. Path may be implemented by a +// real filesystem object (such as File and Dir) or by a type which updates +// entries in a Manifest. type Path interface { Path() string Remove() @@ -45,6 +48,9 @@ func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File { for _, op := range ops { assert.NilError(t, op(file)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(file.Remove) + } return file } @@ -77,6 +83,9 @@ func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir { for _, op := range ops { assert.NilError(t, op(dir)) } + if tc, ok := t.(subtest.TestContext); ok { + tc.AddCleanup(dir.Remove) + } return dir } diff --git a/vendor/gotest.tools/fs/manifest.go b/vendor/gotest.tools/fs/manifest.go new file mode 100644 index 0000000000..00976ef19c --- /dev/null +++ b/vendor/gotest.tools/fs/manifest.go @@ -0,0 +1,129 @@ +package fs + +import ( + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/pkg/errors" + "gotest.tools/assert" +) + +// Manifest stores the expected structure and properties of files and directories +// in a filesystem. +type Manifest struct { + root *directory +} + +type resource struct { + mode os.FileMode + uid uint32 + gid uint32 +} + +type file struct { + resource + content io.ReadCloser +} + +func (f *file) Type() string { + return "file" +} + +type symlink struct { + resource + target string +} + +func (f *symlink) Type() string { + return "symlink" +} + +type directory struct { + resource + items map[string]dirEntry +} + +func (f *directory) Type() string { + return "directory" +} + +type dirEntry interface { + Type() string +} + +// ManifestFromDir creates a Manifest by reading the directory at path. The +// manifest stores the structure and properties of files in the directory. +// ManifestFromDir can be used with Equal to compare two directories. +func ManifestFromDir(t assert.TestingT, path string) Manifest { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + manifest, err := manifestFromDir(path) + assert.NilError(t, err) + return manifest +} + +func manifestFromDir(path string) (Manifest, error) { + info, err := os.Stat(path) + switch { + case err != nil: + return Manifest{}, err + case !info.IsDir(): + return Manifest{}, errors.Errorf("path %s must be a directory", path) + } + + directory, err := newDirectory(path, info) + return Manifest{root: directory}, err +} + +func newDirectory(path string, info os.FileInfo) (*directory, error) { + items := make(map[string]dirEntry) + children, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + for _, child := range children { + fullPath := filepath.Join(path, child.Name()) + items[child.Name()], err = getTypedResource(fullPath, child) + if err != nil { + return nil, err + } + } + + return &directory{ + resource: newResourceFromInfo(info), + items: items, + }, nil +} + +func getTypedResource(path string, info os.FileInfo) (dirEntry, error) { + switch { + case info.IsDir(): + return newDirectory(path, info) + case info.Mode()&os.ModeSymlink != 0: + return newSymlink(path, info) + // TODO: devices, pipes? + default: + return newFile(path, info) + } +} + +func newSymlink(path string, info os.FileInfo) (*symlink, error) { + target, err := os.Readlink(path) + return &symlink{ + resource: newResourceFromInfo(info), + target: target, + }, err +} + +func newFile(path string, info os.FileInfo) (*file, error) { + // TODO: defer file opening to reduce number of open FDs? + readCloser, err := os.Open(path) + return &file{ + resource: newResourceFromInfo(info), + content: readCloser, + }, err +} diff --git a/vendor/gotest.tools/fs/manifest_unix.go b/vendor/gotest.tools/fs/manifest_unix.go new file mode 100644 index 0000000000..bba2fcd9df --- /dev/null +++ b/vendor/gotest.tools/fs/manifest_unix.go @@ -0,0 +1,30 @@ +// +build !windows + +package fs + +import ( + "os" + "syscall" +) + +const ( + defaultRootDirMode = os.ModeDir | 0700 + defaultSymlinkMode = os.ModeSymlink | 0777 +) + +func newResourceFromInfo(info os.FileInfo) resource { + statT := info.Sys().(*syscall.Stat_t) + return resource{ + mode: info.Mode(), + uid: statT.Uid, + gid: statT.Gid, + } +} + +func (p *filePath) SetMode(mode os.FileMode) { + p.file.mode = mode +} + +func (p *directoryPath) SetMode(mode os.FileMode) { + p.directory.mode = mode | os.ModeDir +} diff --git a/vendor/gotest.tools/fs/manifest_windows.go b/vendor/gotest.tools/fs/manifest_windows.go new file mode 100644 index 0000000000..1c1a093679 --- /dev/null +++ b/vendor/gotest.tools/fs/manifest_windows.go @@ -0,0 +1,22 @@ +package fs + +import "os" + +const ( + defaultRootDirMode = os.ModeDir | 0777 + defaultSymlinkMode = os.ModeSymlink | 0666 +) + +func newResourceFromInfo(info os.FileInfo) resource { + return resource{mode: info.Mode()} +} + +func (p *filePath) SetMode(mode os.FileMode) { + bits := mode & 0600 + p.file.mode = bits + bits/010 + bits/0100 +} + +// TODO: is mode ignored on windows? +func (p *directoryPath) SetMode(mode os.FileMode) { + p.directory.mode = defaultRootDirMode +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go b/vendor/gotest.tools/fs/ops.go similarity index 61% rename from vendor/github.com/gotestyourself/gotestyourself/fs/ops.go rename to vendor/gotest.tools/fs/ops.go index bf9d2150b3..ec9d11c110 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go +++ b/vendor/gotest.tools/fs/ops.go @@ -1,32 +1,73 @@ package fs import ( + "bytes" + "io" "io/ioutil" "os" "path/filepath" + "strings" "time" + + "github.com/pkg/errors" ) -// PathOp is a function which accepts a Path to perform some operation +const defaultFileMode = 0644 + +// PathOp is a function which accepts a Path and performs an operation on that +// path. When called with real filesystem objects (File or Dir) a PathOp modifies +// the filesystem at the path. When used with a Manifest object a PathOp updates +// the manifest to expect a value. type PathOp func(path Path) error +type manifestResource interface { + SetMode(mode os.FileMode) + SetUID(uid uint32) + SetGID(gid uint32) +} + +type manifestFile interface { + manifestResource + SetContent(content io.ReadCloser) +} + +type manifestDirectory interface { + manifestResource + AddSymlink(path, target string) error + AddFile(path string, ops ...PathOp) error + AddDirectory(path string, ops ...PathOp) error +} + // WithContent writes content to a file at Path func WithContent(content string) PathOp { return func(path Path) error { - return ioutil.WriteFile(path.Path(), []byte(content), 0644) + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(strings.NewReader(content))) + return nil + } + return ioutil.WriteFile(path.Path(), []byte(content), defaultFileMode) } } // WithBytes write bytes to a file at Path func WithBytes(raw []byte) PathOp { return func(path Path) error { - return ioutil.WriteFile(path.Path(), raw, 0644) + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(bytes.NewReader(raw))) + return nil + } + return ioutil.WriteFile(path.Path(), raw, defaultFileMode) } } // AsUser changes ownership of the file system object at Path func AsUser(uid, gid int) PathOp { return func(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetUID(uint32(uid)) + m.SetGID(uint32(gid)) + return nil + } return os.Chown(path.Path(), uid, gid) } } @@ -34,6 +75,11 @@ func AsUser(uid, gid int) PathOp { // WithFile creates a file in the directory at path with content func WithFile(filename, content string, ops ...PathOp) PathOp { return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + ops = append([]PathOp{WithContent(content), WithMode(defaultFileMode)}, ops...) + return m.AddFile(filename, ops...) + } + fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename)) if err := createFile(fullpath, content); err != nil { return err @@ -43,12 +89,22 @@ func WithFile(filename, content string, ops ...PathOp) PathOp { } func createFile(fullpath string, content string) error { - return ioutil.WriteFile(fullpath, []byte(content), 0644) + return ioutil.WriteFile(fullpath, []byte(content), defaultFileMode) } // WithFiles creates all the files in the directory at path with their content func WithFiles(files map[string]string) PathOp { return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + for filename, content := range files { + // TODO: remove duplication with WithFile + if err := m.AddFile(filename, WithContent(content), WithMode(defaultFileMode)); err != nil { + return err + } + } + return nil + } + for filename, content := range files { fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename)) if err := createFile(fullpath, content); err != nil { @@ -62,6 +118,9 @@ func WithFiles(files map[string]string) PathOp { // FromDir copies the directory tree from the source path into the new Dir func FromDir(source string) PathOp { return func(path Path) error { + if _, ok := path.(manifestDirectory); ok { + return errors.New("use manifest.FromDir") + } return copyDirectory(source, path.Path()) } } @@ -69,9 +128,15 @@ func FromDir(source string) PathOp { // WithDir creates a subdirectory in the directory at path. Additional PathOp // can be used to modify the subdirectory func WithDir(name string, ops ...PathOp) PathOp { + const defaultMode = 0755 return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + ops = append([]PathOp{WithMode(defaultMode)}, ops...) + return m.AddDirectory(name, ops...) + } + fullpath := filepath.Join(path.Path(), filepath.FromSlash(name)) - err := os.MkdirAll(fullpath, 0755) + err := os.MkdirAll(fullpath, defaultMode) if err != nil { return err } @@ -91,6 +156,10 @@ func applyPathOps(path Path, ops []PathOp) error { // WithMode sets the file mode on the directory or file at path func WithMode(mode os.FileMode) PathOp { return func(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetMode(mode) + return nil + } return os.Chmod(path.Path(), mode) } } @@ -112,6 +181,7 @@ func copyDirectory(source, dest string) error { } continue } + // TODO: handle symlinks if err := copyFile(sourcePath, destPath); err != nil { return err } @@ -134,6 +204,9 @@ func copyFile(source, dest string) error { // the other functions in this package. func WithSymlink(path, target string) PathOp { return func(root Path) error { + if v, ok := root.(manifestDirectory); ok { + return v.AddSymlink(path, target) + } return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) } } @@ -145,6 +218,9 @@ func WithSymlink(path, target string) PathOp { // the other functions in this package. func WithHardlink(path, target string) PathOp { return func(root Path) error { + if _, ok := root.(manifestDirectory); ok { + return errors.New("WithHardlink yet implemented for manifests") + } return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) } } @@ -153,6 +229,9 @@ func WithHardlink(path, target string) PathOp { // at path. func WithTimestamps(atime, mtime time.Time) PathOp { return func(root Path) error { + if _, ok := root.(manifestDirectory); ok { + return errors.New("WithTimestamp yet implemented for manifests") + } return os.Chtimes(root.Path(), atime, mtime) } } diff --git a/vendor/gotest.tools/fs/path.go b/vendor/gotest.tools/fs/path.go new file mode 100644 index 0000000000..baf777f0df --- /dev/null +++ b/vendor/gotest.tools/fs/path.go @@ -0,0 +1,151 @@ +package fs + +import ( + "bytes" + "io" + "io/ioutil" + "os" + + "gotest.tools/assert" +) + +// resourcePath is an adaptor for resources so they can be used as a Path +// with PathOps. +type resourcePath struct{} + +func (p *resourcePath) Path() string { + return "manifest: not a filesystem path" +} + +func (p *resourcePath) Remove() {} + +type filePath struct { + resourcePath + file *file +} + +func (p *filePath) SetContent(content io.ReadCloser) { + p.file.content = content +} + +func (p *filePath) SetUID(uid uint32) { + p.file.uid = uid +} + +func (p *filePath) SetGID(gid uint32) { + p.file.gid = gid +} + +type directoryPath struct { + resourcePath + directory *directory +} + +func (p *directoryPath) SetUID(uid uint32) { + p.directory.uid = uid +} + +func (p *directoryPath) SetGID(gid uint32) { + p.directory.gid = gid +} + +func (p *directoryPath) AddSymlink(path, target string) error { + p.directory.items[path] = &symlink{ + resource: newResource(defaultSymlinkMode), + target: target, + } + return nil +} + +func (p *directoryPath) AddFile(path string, ops ...PathOp) error { + newFile := &file{resource: newResource(0)} + p.directory.items[path] = newFile + exp := &filePath{file: newFile} + return applyPathOps(exp, ops) +} + +func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error { + newDir := newDirectoryWithDefaults() + p.directory.items[path] = newDir + exp := &directoryPath{directory: newDir} + return applyPathOps(exp, ops) +} + +// Expected returns a Manifest with a directory structured created by ops. The +// PathOp operations are applied to the manifest as expectations of the +// filesystem structure and properties. +func Expected(t assert.TestingT, ops ...PathOp) Manifest { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + newDir := newDirectoryWithDefaults() + e := &directoryPath{directory: newDir} + assert.NilError(t, applyPathOps(e, ops)) + return Manifest{root: newDir} +} + +func newDirectoryWithDefaults() *directory { + return &directory{ + resource: newResource(defaultRootDirMode), + items: make(map[string]dirEntry), + } +} + +func newResource(mode os.FileMode) resource { + return resource{ + mode: mode, + uid: currentUID(), + gid: currentGID(), + } +} + +func currentUID() uint32 { + return normalizeID(os.Getuid()) +} + +func currentGID() uint32 { + return normalizeID(os.Getgid()) +} + +func normalizeID(id int) uint32 { + // ids will be -1 on windows + if id < 0 { + return 0 + } + return uint32(id) +} + +var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil)) + +// MatchAnyFileContent is a PathOp that updates a Manifest so that the file +// at path may contain any content. +func MatchAnyFileContent(path Path) error { + if m, ok := path.(*filePath); ok { + m.SetContent(anyFileContent) + } + return nil +} + +const anyFile = "*" + +// MatchExtraFiles is a PathOp that updates a Manifest to allow a directory +// to contain unspecified files. +func MatchExtraFiles(path Path) error { + if m, ok := path.(*directoryPath); ok { + m.AddFile(anyFile) + } + return nil +} + +// anyFileMode is represented by uint32_max +const anyFileMode os.FileMode = 4294967295 + +// MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path +// will match any file mode. +func MatchAnyFileMode(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetMode(anyFileMode) + } + return nil +} diff --git a/vendor/gotest.tools/fs/report.go b/vendor/gotest.tools/fs/report.go new file mode 100644 index 0000000000..4de85a6ef7 --- /dev/null +++ b/vendor/gotest.tools/fs/report.go @@ -0,0 +1,215 @@ +package fs + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" +) + +// Equal compares a directory to the expected structured described by a manifest +// and returns success if they match. If they do not match the failure message +// will contain all the differences between the directory structure and the +// expected structure defined by the Manifest. +// +// Equal is a cmp.Comparison which can be used with assert.Assert(). +func Equal(path string, expected Manifest) cmp.Comparison { + return func() cmp.Result { + actual, err := manifestFromDir(path) + if err != nil { + return cmp.ResultFromError(err) + } + failures := eqDirectory(string(os.PathSeparator), expected.root, actual.root) + if len(failures) == 0 { + return cmp.ResultSuccess + } + msg := fmt.Sprintf("directory %s does not match expected:\n", path) + return cmp.ResultFailure(msg + formatFailures(failures)) + } +} + +type failure struct { + path string + problems []problem +} + +type problem string + +func notEqual(property string, x, y interface{}) problem { + return problem(fmt.Sprintf("%s: expected %s got %s", property, x, y)) +} + +func errProblem(reason string, err error) problem { + return problem(fmt.Sprintf("%s: %s", reason, err)) +} + +func existenceProblem(filename, reason string, args ...interface{}) problem { + return problem(filename + ": " + fmt.Sprintf(reason, args...)) +} + +func eqResource(x, y resource) []problem { + var p []problem + if x.uid != y.uid { + p = append(p, notEqual("uid", x.uid, y.uid)) + } + if x.gid != y.gid { + p = append(p, notEqual("gid", x.gid, y.gid)) + } + if x.mode != anyFileMode && x.mode != y.mode { + p = append(p, notEqual("mode", x.mode, y.mode)) + } + return p +} + +func eqFile(x, y *file) []problem { + p := eqResource(x.resource, y.resource) + + switch { + case x.content == nil: + p = append(p, existenceProblem("content", "expected content is nil")) + return p + case x.content == anyFileContent: + return p + case y.content == nil: + p = append(p, existenceProblem("content", "actual content is nil")) + return p + } + + xContent, xErr := ioutil.ReadAll(x.content) + defer x.content.Close() + yContent, yErr := ioutil.ReadAll(y.content) + defer y.content.Close() + + if xErr != nil { + p = append(p, errProblem("failed to read expected content", xErr)) + } + if yErr != nil { + p = append(p, errProblem("failed to read actual content", xErr)) + } + if xErr != nil || yErr != nil { + return p + } + + if !bytes.Equal(xContent, yContent) { + p = append(p, diffContent(xContent, yContent)) + } + return p +} + +func diffContent(x, y []byte) problem { + diff := format.UnifiedDiff(format.DiffConfig{ + A: string(x), + B: string(y), + From: "expected", + To: "actual", + }) + // Remove the trailing newline in the diff. A trailing newline is always + // added to a problem by formatFailures. + diff = strings.TrimSuffix(diff, "\n") + return problem("content:\n" + indent(diff, " ")) +} + +func indent(s, prefix string) string { + buf := new(bytes.Buffer) + lines := strings.SplitAfter(s, "\n") + for _, line := range lines { + buf.WriteString(prefix + line) + } + return buf.String() +} + +func eqSymlink(x, y *symlink) []problem { + p := eqResource(x.resource, y.resource) + if x.target != y.target { + p = append(p, notEqual("target", x.target, y.target)) + } + return p +} + +func eqDirectory(path string, x, y *directory) []failure { + p := eqResource(x.resource, y.resource) + var f []failure + + for _, name := range sortedKeys(x.items) { + if name == anyFile { + continue + } + xEntry := x.items[name] + yEntry, ok := y.items[name] + if !ok { + p = append(p, existenceProblem(name, "expected %s to exist", xEntry.Type())) + continue + } + + if xEntry.Type() != yEntry.Type() { + p = append(p, notEqual(name, xEntry.Type(), yEntry.Type())) + continue + } + + f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...) + } + + if _, ok := x.items[anyFile]; !ok { + for _, name := range sortedKeys(y.items) { + if _, ok := x.items[name]; !ok { + yEntry := y.items[name] + p = append(p, existenceProblem(name, "unexpected %s", yEntry.Type())) + } + } + } + + if len(p) > 0 { + f = append(f, failure{path: path, problems: p}) + } + return f +} + +func sortedKeys(items map[string]dirEntry) []string { + var keys []string + for key := range items { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +// eqEntry assumes x and y to be the same type +func eqEntry(path string, x, y dirEntry) []failure { + resp := func(problems []problem) []failure { + if len(problems) == 0 { + return nil + } + return []failure{{path: path, problems: problems}} + } + + switch typed := x.(type) { + case *file: + return resp(eqFile(typed, y.(*file))) + case *symlink: + return resp(eqSymlink(typed, y.(*symlink))) + case *directory: + return eqDirectory(path, typed, y.(*directory)) + } + return nil +} + +func formatFailures(failures []failure) string { + sort.Slice(failures, func(i, j int) bool { + return failures[i].path < failures[j].path + }) + + buf := new(bytes.Buffer) + for _, failure := range failures { + buf.WriteString(failure.path + "\n") + for _, problem := range failure.problems { + buf.WriteString(" " + string(problem) + "\n") + } + } + return buf.String() +} diff --git a/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go b/vendor/gotest.tools/golden/golden.go similarity index 76% rename from vendor/github.com/gotestyourself/gotestyourself/golden/golden.go rename to vendor/gotest.tools/golden/golden.go index 357edcfd44..1804b65931 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go +++ b/vendor/gotest.tools/golden/golden.go @@ -2,7 +2,7 @@ Golden files are files in the ./testdata/ subdirectory of the package under test. */ -package golden +package golden // import "gotest.tools/golden" import ( "bytes" @@ -11,9 +11,9 @@ import ( "io/ioutil" "path/filepath" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/assert/cmp" - "github.com/pmezard/go-difflib/difflib" + "gotest.tools/assert" + "gotest.tools/assert/cmp" + "gotest.tools/internal/format" ) var flagUpdate = flag.Bool("test.update-golden", false, "update golden file") @@ -62,16 +62,11 @@ func exactBytes(in []byte) []byte { // to the golden file. // Returns whether the assertion was successful (true) or not (false). // This is equivalent to assert.Check(t, String(actual, filename)) -// -// Deprecated: In a future version this function will change to use assert.Assert -// instead of assert.Check to be consistent with other assert functions. -// Use assert.Check(t, String(actual, filename) if you want to preserve the -// current behaviour. -func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) bool { +func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() } - return assert.Check(t, String(actual, filename), msgAndArgs...) + assert.Assert(t, String(actual, filename), msgAndArgs...) } // String compares actual to the contents of filename and returns success @@ -90,16 +85,12 @@ func String(actual string, filename string) cmp.Comparison { if result != nil { return result } - diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(string(expected)), - B: difflib.SplitLines(string(actualBytes)), - FromFile: "expected", - ToFile: "actual", - Context: 3, + diff := format.UnifiedDiff(format.DiffConfig{ + A: string(expected), + B: string(actualBytes), + From: "expected", + To: "actual", }) - if err != nil { - return cmp.ResultFromError(err) - } return cmp.ResultFailure("\n" + diff) } } @@ -109,21 +100,16 @@ func String(actual string, filename string) cmp.Comparison { // written to the golden file. // Returns whether the assertion was successful (true) or not (false) // This is equivalent to assert.Check(t, Bytes(actual, filename)) -// -// Deprecated: In a future version this function will change to use assert.Assert -// instead of assert.Check to be consistent with other assert functions. -// Use assert.Check(t, Bytes(actual, filename) if you want to preserve the -// current behaviour. func AssertBytes( t assert.TestingT, actual []byte, filename string, msgAndArgs ...interface{}, -) bool { +) { if ht, ok := t.(helperT); ok { ht.Helper() } - return assert.Check(t, Bytes(actual, filename), msgAndArgs...) + assert.Assert(t, Bytes(actual, filename), msgAndArgs...) } // Bytes compares actual to the contents of filename and returns success diff --git a/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go b/vendor/gotest.tools/icmd/command.go similarity index 98% rename from vendor/github.com/gotestyourself/gotestyourself/icmd/command.go rename to vendor/gotest.tools/icmd/command.go index 0066c51514..901895991a 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go +++ b/vendor/gotest.tools/icmd/command.go @@ -1,6 +1,6 @@ /*Package icmd executes binaries and provides convenient assertions for testing the results. */ -package icmd +package icmd // import "gotest.tools/icmd" import ( "bytes" @@ -11,8 +11,8 @@ import ( "sync" "time" - "github.com/gotestyourself/gotestyourself/assert" - "github.com/gotestyourself/gotestyourself/assert/cmp" + "gotest.tools/assert" + "gotest.tools/assert/cmp" ) type helperT interface { @@ -228,6 +228,7 @@ func StartCmd(cmd Cmd) *Result { return result } +// TODO: support exec.CommandContext func buildCmd(cmd Cmd) *Result { var execCmd *exec.Cmd switch len(cmd.Command) { diff --git a/vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go b/vendor/gotest.tools/icmd/exitcode.go similarity index 100% rename from vendor/github.com/gotestyourself/gotestyourself/icmd/exitcode.go rename to vendor/gotest.tools/icmd/exitcode.go diff --git a/vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go b/vendor/gotest.tools/icmd/ops.go similarity index 100% rename from vendor/github.com/gotestyourself/gotestyourself/icmd/ops.go rename to vendor/gotest.tools/icmd/ops.go diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/gotest.tools/internal/difflib/LICENSE similarity index 100% rename from vendor/github.com/pmezard/go-difflib/LICENSE rename to vendor/gotest.tools/internal/difflib/LICENSE diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/gotest.tools/internal/difflib/difflib.go similarity index 56% rename from vendor/github.com/pmezard/go-difflib/difflib/difflib.go rename to vendor/gotest.tools/internal/difflib/difflib.go index 003e99fadb..5efa99c1d4 100644 --- a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go +++ b/vendor/gotest.tools/internal/difflib/difflib.go @@ -1,27 +1,10 @@ -// Package difflib is a partial port of Python difflib module. -// -// It provides tools to compare sequences of strings and generate textual diffs. -// -// The following class and functions have been ported: -// -// - SequenceMatcher -// -// - unified_diff -// -// - context_diff -// -// Getting unified diffs was the main goal of the port. Keep in mind this code -// is mostly suitable to output text differences in a human friendly way, there -// are no guarantees generated diffs are consumable by patch(1). -package difflib +/* Package difflib is a partial port of Python difflib module. -import ( - "bufio" - "bytes" - "fmt" - "io" - "strings" -) +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/internal/difflib" func min(a, b int) int { if a < b { @@ -37,13 +20,6 @@ func max(a, b int) int { return b } -func calculateRatio(matches, length int) float64 { - if length > 0 { - return 2.0 * float64(matches) / float64(length) - } - return 1.0 -} - type Match struct { A int B int @@ -103,14 +79,6 @@ func NewMatcher(a, b []string) *SequenceMatcher { return &m } -func NewMatcherWithJunk(a, b []string, autoJunk bool, - isJunk func(string) bool) *SequenceMatcher { - - m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} - m.SetSeqs(a, b) - return &m -} - // Set two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) @@ -450,323 +418,3 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { } return groups } - -// Return a measure of the sequences' similarity (float in [0,1]). -// -// Where T is the total number of elements in both sequences, and -// M is the number of matches, this is 2.0*M / T. -// Note that this is 1 if the sequences are identical, and 0 if -// they have nothing in common. -// -// .Ratio() is expensive to compute if you haven't already computed -// .GetMatchingBlocks() or .GetOpCodes(), in which case you may -// want to try .QuickRatio() or .RealQuickRation() first to get an -// upper bound. -func (m *SequenceMatcher) Ratio() float64 { - matches := 0 - for _, m := range m.GetMatchingBlocks() { - matches += m.Size - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() relatively quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute. -func (m *SequenceMatcher) QuickRatio() float64 { - // viewing a and b as multisets, set matches to the cardinality - // of their intersection; this counts the number of matches - // without regard to order, so is clearly an upper bound - if m.fullBCount == nil { - m.fullBCount = map[string]int{} - for _, s := range m.b { - m.fullBCount[s] = m.fullBCount[s] + 1 - } - } - - // avail[x] is the number of times x appears in 'b' less the - // number of times we've seen it in 'a' so far ... kinda - avail := map[string]int{} - matches := 0 - for _, s := range m.a { - n, ok := avail[s] - if !ok { - n = m.fullBCount[s] - } - avail[s] = n - 1 - if n > 0 { - matches += 1 - } - } - return calculateRatio(matches, len(m.a)+len(m.b)) -} - -// Return an upper bound on ratio() very quickly. -// -// This isn't defined beyond that it is an upper bound on .Ratio(), and -// is faster to compute than either .Ratio() or .QuickRatio(). -func (m *SequenceMatcher) RealQuickRatio() float64 { - la, lb := len(m.a), len(m.b) - return calculateRatio(min(la, lb), la+lb) -} - -// 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 -= 1 // empty ranges begin at line just before the range - } - return fmt.Sprintf("%d,%d", beginning, length) -} - -// Unified diff parameters -type UnifiedDiff struct { - A []string // First sequence lines - FromFile string // First file name - FromDate string // First file time - B []string // Second sequence lines - ToFile string // Second file name - ToDate string // Second file time - Eol string // Headers end of line, defaults to LF - Context int // Number of context lines -} - -// Compare two sequences of lines; generate the delta as a unified diff. -// -// Unified diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by 'n' which -// defaults to three. -// -// By default, the diff control lines (those with ---, +++, or @@) are -// created with a trailing newline. This is helpful so that inputs -// created from file.readlines() result in diffs that are suitable for -// file.writelines() since both the inputs and outputs have trailing -// newlines. -// -// For inputs that do not have trailing newlines, set the lineterm -// argument to "" so that the output will be uniformly newline free. -// -// The unidiff format normally has a header for filenames and modification -// times. Any or all of these may be specified using strings for -// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. -// The modification times are normally expressed in the ISO 8601 format. -func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - wf := func(format string, args ...interface{}) error { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - return err - } - ws := func(s string) error { - _, err := buf.WriteString(s) - return err - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) - if err != nil { - return err - } - err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) - if err != nil { - return err - } - } - } - first, last := g[0], g[len(g)-1] - range1 := formatRangeUnified(first.I1, last.I2) - range2 := formatRangeUnified(first.J1, last.J2) - if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { - return err - } - for _, c := range g { - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - if c.Tag == 'e' { - for _, line := range diff.A[i1:i2] { - if err := ws(" " + line); err != nil { - return err - } - } - continue - } - if c.Tag == 'r' || c.Tag == 'd' { - for _, line := range diff.A[i1:i2] { - if err := ws("-" + line); err != nil { - return err - } - } - } - if c.Tag == 'r' || c.Tag == 'i' { - for _, line := range diff.B[j1:j2] { - if err := ws("+" + line); err != nil { - return err - } - } - } - } - } - return nil -} - -// Like WriteUnifiedDiff but returns the diff a string. -func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteUnifiedDiff(w, diff) - return string(w.Bytes()), err -} - -// Convert range to the "ed" format. -func formatRangeContext(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 == 0 { - beginning -= 1 // empty ranges begin at line just before the range - } - if length <= 1 { - return fmt.Sprintf("%d", beginning) - } - return fmt.Sprintf("%d,%d", beginning, beginning+length-1) -} - -type ContextDiff UnifiedDiff - -// Compare two sequences of lines; generate the delta as a context diff. -// -// Context diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by diff.Context -// which defaults to three. -// -// By default, the diff control lines (those with *** or ---) are -// created with a trailing newline. -// -// For inputs that do not have trailing newlines, set the diff.Eol -// argument to "" so that the output will be uniformly newline free. -// -// The context diff format normally has a header for filenames and -// modification times. Any or all of these may be specified using -// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. -// The modification times are normally expressed in the ISO 8601 format. -// If not specified, the strings default to blanks. -func WriteContextDiff(writer io.Writer, diff ContextDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - var diffErr error - wf := func(format string, args ...interface{}) { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - if diffErr == nil && err != nil { - diffErr = err - } - } - ws := func(s string) { - _, err := buf.WriteString(s) - if diffErr == nil && err != nil { - diffErr = err - } - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - prefix := map[byte]string{ - 'i': "+ ", - 'd': "- ", - 'r': "! ", - 'e': " ", - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) - wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) - } - } - - first, last := g[0], g[len(g)-1] - ws("***************" + diff.Eol) - - range1 := formatRangeContext(first.I1, last.I2) - wf("*** %s ****%s", range1, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'd' { - for _, cc := range g { - if cc.Tag == 'i' { - continue - } - for _, line := range diff.A[cc.I1:cc.I2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - - range2 := formatRangeContext(first.J1, last.J2) - wf("--- %s ----%s", range2, diff.Eol) - for _, c := range g { - if c.Tag == 'r' || c.Tag == 'i' { - for _, cc := range g { - if cc.Tag == 'd' { - continue - } - for _, line := range diff.B[cc.J1:cc.J2] { - ws(prefix[cc.Tag] + line) - } - } - break - } - } - } - return diffErr -} - -// Like WriteContextDiff but returns the diff a string. -func GetContextDiffString(diff ContextDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteContextDiff(w, diff) - return string(w.Bytes()), err -} - -// Split a string on "\n" while preserving them. The output can be used -// as input for UnifiedDiff and ContextDiff structures. -func SplitLines(s string) []string { - lines := strings.SplitAfter(s, "\n") - lines[len(lines)-1] += "\n" - return lines -} diff --git a/vendor/gotest.tools/internal/format/diff.go b/vendor/gotest.tools/internal/format/diff.go new file mode 100644 index 0000000000..c938c97bec --- /dev/null +++ b/vendor/gotest.tools/internal/format/diff.go @@ -0,0 +1,161 @@ +package format + +import ( + "bytes" + "fmt" + "strings" + "unicode" + + "gotest.tools/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/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go b/vendor/gotest.tools/internal/format/format.go similarity index 91% rename from vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go rename to vendor/gotest.tools/internal/format/format.go index 6961ffe6da..8f6494f994 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go +++ b/vendor/gotest.tools/internal/format/format.go @@ -1,4 +1,4 @@ -package format +package format // import "gotest.tools/internal/format" import "fmt" diff --git a/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go b/vendor/gotest.tools/internal/source/source.go similarity index 98% rename from vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go rename to vendor/gotest.tools/internal/source/source.go index f71c5129b8..a05933cc33 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go +++ b/vendor/gotest.tools/internal/source/source.go @@ -1,4 +1,4 @@ -package source +package source // import "gotest.tools/internal/source" import ( "bytes" diff --git a/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go b/vendor/gotest.tools/poll/poll.go similarity index 95% rename from vendor/github.com/gotestyourself/gotestyourself/poll/poll.go rename to vendor/gotest.tools/poll/poll.go index 42a42cc149..3e3dd7f58a 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go +++ b/vendor/gotest.tools/poll/poll.go @@ -1,6 +1,6 @@ /*Package poll provides tools for testing asynchronous code. */ -package poll +package poll // import "gotest.tools/poll" import ( "fmt" @@ -25,15 +25,15 @@ type helperT interface { // 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 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. Detaults to - // 1ms + // 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: time.Millisecond} + return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond} } // SettingOp is a function which accepts and modifies Settings diff --git a/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go b/vendor/gotest.tools/skip/skip.go similarity index 60% rename from vendor/github.com/gotestyourself/gotestyourself/skip/skip.go rename to vendor/gotest.tools/skip/skip.go index 46ab288819..4bfc9a11c2 100644 --- a/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go +++ b/vendor/gotest.tools/skip/skip.go @@ -1,6 +1,7 @@ -/*Package skip provides functions for skipping based on a condition. - */ -package skip +/*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/skip" import ( "fmt" @@ -9,8 +10,8 @@ import ( "runtime" "strings" - "github.com/gotestyourself/gotestyourself/internal/format" - "github.com/gotestyourself/gotestyourself/internal/source" + "gotest.tools/internal/format" + "gotest.tools/internal/source" ) type skipT interface { @@ -25,9 +26,10 @@ type helperT interface { // BoolOrCheckFunc can be a bool or func() bool, other types will panic type BoolOrCheckFunc interface{} -// If skips the test if the check function returns true. The skip message will -// contain the name of the check function. Extra message text can be passed as a -// format string with args +// If the condition expression evaluates to true, or the condition function returns +// true, skip the test. +// 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() @@ -49,18 +51,6 @@ func getFunctionName(function func() bool) string { return strings.SplitN(path.Base(funcPath), ".", 2)[1] } -// IfCondition skips the test if the condition is true. The skip message will -// contain the source of the expression passed as the condition. Extra message -// text can be passed as a format string with args. -// -// Deprecated: Use If() which now accepts bool arguments -func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) { - if ht, ok := t.(helperT); ok { - ht.Helper() - } - ifCondition(t, condition, msgAndArgs...) -} - func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) { if ht, ok := t.(helperT); ok { ht.Helper() diff --git a/vendor/gotest.tools/x/subtest/context.go b/vendor/gotest.tools/x/subtest/context.go new file mode 100644 index 0000000000..878bdebf14 --- /dev/null +++ b/vendor/gotest.tools/x/subtest/context.go @@ -0,0 +1,81 @@ +/*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. +*/ +package subtest // import "gotest.tools/x/subtest" + +import ( + "context" + "testing" +) + +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()) + tc.AddCleanup(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. + 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{}