mirror of https://github.com/docker/cli.git
Merge pull request #1825 from thaJeztah/bump_gotest_2.3.0
bump gotest.tools v2.3.0
This commit is contained in:
commit
f02d94afbb
|
@ -89,7 +89,7 @@ google.golang.org/genproto 02b4e95473316948020af0b7a4f0
|
|||
google.golang.org/grpc 41344da2231b913fa3d983840a57a6b1b7b631a1 # v1.12.0
|
||||
gopkg.in/inf.v0 d2d2541c53f18d2a059457998ce2876cc8e67cbf # v0.9.1
|
||||
gopkg.in/yaml.v2 5420a8b6744d3b0345ab293f6fcba19c978f1183 # v2.2.1
|
||||
gotest.tools 7c797b5133e5460410dbb22ba779bf35e6975dea # v2.2.0
|
||||
gotest.tools 1083505acf35a0bd8a696b26837e1fb3187a7a83 # v2.3.0
|
||||
k8s.io/api 40a48860b5abbba9aa891b02b32da429b08d96a0 # kubernetes-1.14.0
|
||||
k8s.io/apimachinery d7deff9243b165ee192f5551710ea4285dcfd615 # kubernetes-1.14.0
|
||||
k8s.io/client-go 6ee68ca5fd8355d024d02f9db0b3b667e8357a0f # kubernetes-1.14.0
|
||||
|
|
|
@ -29,3 +29,7 @@ A collection of packages to augment `testing` and support common patterns.
|
|||
* [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`
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
|
|
@ -9,31 +9,37 @@ import (
|
|||
"gotest.tools/internal/source"
|
||||
)
|
||||
|
||||
// Result of a Comparison.
|
||||
// A Result of a Comparison.
|
||||
type Result interface {
|
||||
Success() bool
|
||||
}
|
||||
|
||||
type result struct {
|
||||
// StringResult is an implementation of Result that reports the error message
|
||||
// string verbatim and does not provide any templating or formatting of the
|
||||
// message.
|
||||
type StringResult struct {
|
||||
success bool
|
||||
message string
|
||||
}
|
||||
|
||||
func (r result) Success() bool {
|
||||
// Success returns true if the comparison was successful.
|
||||
func (r StringResult) Success() bool {
|
||||
return r.success
|
||||
}
|
||||
|
||||
func (r result) FailureMessage() string {
|
||||
// FailureMessage returns the message used to provide additional information
|
||||
// about the failure.
|
||||
func (r StringResult) FailureMessage() string {
|
||||
return r.message
|
||||
}
|
||||
|
||||
// ResultSuccess is a constant which is returned by a ComparisonWithResult to
|
||||
// indicate success.
|
||||
var ResultSuccess = result{success: true}
|
||||
var ResultSuccess = StringResult{success: true}
|
||||
|
||||
// ResultFailure returns a failed Result with a failure message.
|
||||
func ResultFailure(message string) Result {
|
||||
return result{message: message}
|
||||
func ResultFailure(message string) StringResult {
|
||||
return StringResult{message: message}
|
||||
}
|
||||
|
||||
// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure
|
||||
|
|
|
@ -46,10 +46,7 @@ func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File {
|
|||
assert.NilError(t, err)
|
||||
file := &File{path: tempfile.Name()}
|
||||
assert.NilError(t, tempfile.Close())
|
||||
|
||||
for _, op := range ops {
|
||||
assert.NilError(t, op(file))
|
||||
}
|
||||
assert.NilError(t, applyPathOps(file, ops))
|
||||
if tc, ok := t.(subtest.TestContext); ok {
|
||||
tc.AddCleanup(file.Remove)
|
||||
}
|
||||
|
@ -89,10 +86,7 @@ func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir {
|
|||
path, err := ioutil.TempDir("", cleanPrefix(prefix)+"-")
|
||||
assert.NilError(t, err)
|
||||
dir := &Dir{path: path}
|
||||
|
||||
for _, op := range ops {
|
||||
assert.NilError(t, op(dir))
|
||||
}
|
||||
assert.NilError(t, applyPathOps(dir, ops))
|
||||
if tc, ok := t.(subtest.TestContext); ok {
|
||||
tc.AddCleanup(dir.Remove)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ type file struct {
|
|||
resource
|
||||
content io.ReadCloser
|
||||
ignoreCariageReturn bool
|
||||
compareContentFunc func(b []byte) CompareResult
|
||||
}
|
||||
|
||||
func (f *file) Type() string {
|
||||
|
@ -43,7 +44,8 @@ func (f *symlink) Type() string {
|
|||
|
||||
type directory struct {
|
||||
resource
|
||||
items map[string]dirEntry
|
||||
items map[string]dirEntry
|
||||
filepathGlobs map[string]*filePath
|
||||
}
|
||||
|
||||
func (f *directory) Type() string {
|
||||
|
@ -95,8 +97,9 @@ func newDirectory(path string, info os.FileInfo) (*directory, error) {
|
|||
}
|
||||
|
||||
return &directory{
|
||||
resource: newResourceFromInfo(info),
|
||||
items: items,
|
||||
resource: newResourceFromInfo(info),
|
||||
items: items,
|
||||
filepathGlobs: make(map[string]*filePath),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
const defaultFileMode = 0644
|
||||
|
@ -144,6 +145,14 @@ func WithDir(name string, ops ...PathOp) PathOp {
|
|||
}
|
||||
}
|
||||
|
||||
// Apply the PathOps to the File
|
||||
func Apply(t assert.TestingT, path Path, ops ...PathOp) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
assert.NilError(t, applyPathOps(path, ops))
|
||||
}
|
||||
|
||||
func applyPathOps(path Path, ops []PathOp) error {
|
||||
for _, op := range ops {
|
||||
if err := op(path); err != nil {
|
||||
|
|
|
@ -64,6 +64,13 @@ func (p *directoryPath) AddFile(path string, ops ...PathOp) error {
|
|||
return applyPathOps(exp, ops)
|
||||
}
|
||||
|
||||
func (p *directoryPath) AddGlobFiles(glob string, ops ...PathOp) error {
|
||||
newFile := &file{resource: newResource(0)}
|
||||
newFilePath := &filePath{file: newFile}
|
||||
p.directory.filepathGlobs[glob] = newFilePath
|
||||
return applyPathOps(newFilePath, ops)
|
||||
}
|
||||
|
||||
func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error {
|
||||
newDir := newDirectoryWithDefaults()
|
||||
p.directory.items[path] = newDir
|
||||
|
@ -87,8 +94,9 @@ func Expected(t assert.TestingT, ops ...PathOp) Manifest {
|
|||
|
||||
func newDirectoryWithDefaults() *directory {
|
||||
return &directory{
|
||||
resource: newResource(defaultRootDirMode),
|
||||
items: make(map[string]dirEntry),
|
||||
resource: newResource(defaultRootDirMode),
|
||||
items: make(map[string]dirEntry),
|
||||
filepathGlobs: make(map[string]*filePath),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,6 +155,37 @@ func MatchExtraFiles(path Path) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CompareResult is the result of comparison.
|
||||
//
|
||||
// See gotest.tools/assert/cmp.StringResult for a convenient implementation of
|
||||
// this interface.
|
||||
type CompareResult interface {
|
||||
Success() bool
|
||||
FailureMessage() string
|
||||
}
|
||||
|
||||
// MatchFileContent is a PathOp that updates a Manifest to use the provided
|
||||
// function to determine if a file's content matches the expectation.
|
||||
func MatchFileContent(f func([]byte) CompareResult) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(*filePath); ok {
|
||||
m.file.compareContentFunc = f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MatchFilesWithGlob is a PathOp that updates a Manifest to match files using
|
||||
// glob pattern, and check them using the ops.
|
||||
func MatchFilesWithGlob(glob string, ops ...PathOp) PathOp {
|
||||
return func(path Path) error {
|
||||
if m, ok := path.(*directoryPath); ok {
|
||||
m.AddGlobFiles(glob, ops...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// anyFileMode is represented by uint32_max
|
||||
const anyFileMode os.FileMode = 4294967295
|
||||
|
||||
|
|
|
@ -101,6 +101,15 @@ func eqFile(x, y *file) []problem {
|
|||
if xErr != nil || yErr != nil {
|
||||
return p
|
||||
}
|
||||
|
||||
if x.compareContentFunc != nil {
|
||||
r := x.compareContentFunc(yContent)
|
||||
if !r.Success() {
|
||||
p = append(p, existenceProblem("content", r.FailureMessage()))
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
if x.ignoreCariageReturn || y.ignoreCariageReturn {
|
||||
xContent = removeCarriageReturn(xContent)
|
||||
yContent = removeCarriageReturn(yContent)
|
||||
|
@ -151,11 +160,13 @@ func eqSymlink(x, y *symlink) []problem {
|
|||
func eqDirectory(path string, x, y *directory) []failure {
|
||||
p := eqResource(x.resource, y.resource)
|
||||
var f []failure
|
||||
matchedFiles := make(map[string]bool)
|
||||
|
||||
for _, name := range sortedKeys(x.items) {
|
||||
if name == anyFile {
|
||||
continue
|
||||
}
|
||||
matchedFiles[name] = true
|
||||
xEntry := x.items[name]
|
||||
yEntry, ok := y.items[name]
|
||||
if !ok {
|
||||
|
@ -171,19 +182,30 @@ func eqDirectory(path string, x, y *directory) []failure {
|
|||
f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...)
|
||||
}
|
||||
|
||||
if _, ok := x.items[anyFile]; !ok {
|
||||
if len(x.filepathGlobs) != 0 {
|
||||
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()))
|
||||
}
|
||||
m := matchGlob(name, y.items[name], x.filepathGlobs)
|
||||
matchedFiles[name] = m.match
|
||||
f = append(f, m.failures...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
f = append(f, failure{path: path, problems: p})
|
||||
if _, ok := x.items[anyFile]; ok {
|
||||
return maybeAppendFailure(f, path, p)
|
||||
}
|
||||
return f
|
||||
for _, name := range sortedKeys(y.items) {
|
||||
if !matchedFiles[name] {
|
||||
p = append(p, existenceProblem(name, "unexpected %s", y.items[name].Type()))
|
||||
}
|
||||
}
|
||||
return maybeAppendFailure(f, path, p)
|
||||
}
|
||||
|
||||
func maybeAppendFailure(failures []failure, path string, problems []problem) []failure {
|
||||
if len(problems) > 0 {
|
||||
return append(failures, failure{path: path, problems: problems})
|
||||
}
|
||||
return failures
|
||||
}
|
||||
|
||||
func sortedKeys(items map[string]dirEntry) []string {
|
||||
|
@ -215,6 +237,30 @@ func eqEntry(path string, x, y dirEntry) []failure {
|
|||
return nil
|
||||
}
|
||||
|
||||
type globMatch struct {
|
||||
match bool
|
||||
failures []failure
|
||||
}
|
||||
|
||||
func matchGlob(name string, yEntry dirEntry, globs map[string]*filePath) globMatch {
|
||||
m := globMatch{}
|
||||
|
||||
for glob, expectedFile := range globs {
|
||||
ok, err := filepath.Match(glob, name)
|
||||
if err != nil {
|
||||
p := errProblem("failed to match glob pattern", err)
|
||||
f := failure{path: name, problems: []problem{p}}
|
||||
m.failures = append(m.failures, f)
|
||||
}
|
||||
if ok {
|
||||
m.match = true
|
||||
m.failures = eqEntry(name, expectedFile.file, yEntry)
|
||||
return m
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func formatFailures(failures []failure) string {
|
||||
sort.Slice(failures, func(i, j int) bool {
|
||||
return failures[i].path < failures[j].path
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
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 {
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
t.Logf("waiting on file %s to exist", path)
|
||||
return Continue("file %s does not exist", path)
|
||||
}
|
||||
if err != nil {
|
||||
return Error(err)
|
||||
}
|
||||
|
||||
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 {
|
||||
_, 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()
|
||||
}
|
||||
}
|
|
@ -104,7 +104,7 @@ func Error(err error) Result {
|
|||
// 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 func(t LogT) Result, pollOps ...SettingOp) {
|
||||
func WaitOn(t TestingT, check Check, pollOps ...SettingOp) {
|
||||
if ht, ok := t.(helperT); ok {
|
||||
ht.Helper()
|
||||
}
|
||||
|
|
|
@ -19,17 +19,29 @@ type skipT interface {
|
|||
Log(args ...interface{})
|
||||
}
|
||||
|
||||
// Result of skip function
|
||||
type Result interface {
|
||||
Skip() bool
|
||||
Message() string
|
||||
}
|
||||
|
||||
type helperT interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
// BoolOrCheckFunc can be a bool or func() bool, other types will panic
|
||||
// BoolOrCheckFunc can be a bool, func() bool, or func() Result. Other types will panic
|
||||
type BoolOrCheckFunc interface{}
|
||||
|
||||
// If the condition expression evaluates to true, or the condition function returns
|
||||
// true, skip the test.
|
||||
// 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
|
||||
// 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()
|
||||
|
@ -41,12 +53,18 @@ func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
|
|||
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 func() bool) string {
|
||||
func getFunctionName(function interface{}) string {
|
||||
funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()
|
||||
return strings.SplitN(path.Base(funcPath), ".", 2)[1]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue