Browse code

vendor: gotest.tools v3.0.3

- assert: fixes a bug that would cause a panic if there were any
function calls before `assert.Check` on the same line
- golden: create the directory if it does not exist, when run with
`-test.update-golden`

full diff: https://github.com/gotestyourself/gotest.tools/compare/v3.0.2...v3.0.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2020/11/23 20:33:57
Showing 13 changed files
... ...
@@ -24,7 +24,7 @@ golang.org/x/sys                                    eeed37f84f13f52d35e095e8023b
24 24
 github.com/docker/go-units                          519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0
25 25
 github.com/docker/go-connections                    7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0
26 26
 golang.org/x/text                                   23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
27
-gotest.tools/v3                                     bb0d8a963040ea5048dcef1a14d8f8b58a33d4b3 # v3.0.2
27
+gotest.tools/v3                                     568bc57cc5c19a2ef85e5749870b49a4cc2ab54d # v3.0.3
28 28
 github.com/google/go-cmp                            3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0
29 29
 github.com/syndtr/gocapability                      42c35b4376354fd554efc7ad35e0b7f94e3a0ffb
30 30
 
... ...
@@ -2,7 +2,7 @@
2 2
 
3 3
 A collection of packages to augment `testing` and support common patterns.
4 4
 
5
-[![GoDoc](https://godoc.org/gotest.tools?status.svg)](http://gotest.tools)
5
+[![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://pkg.go.dev/gotest.tools/v3/?tab=subdirectories)
6 6
 [![CircleCI](https://circleci.com/gh/gotestyourself/gotest.tools/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotest.tools/tree/master)
7 7
 [![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools)
8 8
 
... ...
@@ -24,19 +24,19 @@ module paths pin to version `v2.3.0`.
24 24
 
25 25
 ## Packages
26 26
 
27
-* [assert](http://gotest.tools/assert) -
27
+* [assert](http://pkg.go.dev/gotest.tools/v3/assert) -
28 28
   compare values and fail the test when a comparison fails
29
-* [env](http://gotest.tools/env) -
29
+* [env](http://pkg.go.dev/gotest.tools/v3/env) -
30 30
   test code which uses environment variables
31
-* [fs](http://gotest.tools/fs) -
31
+* [fs](http://pkg.go.dev/gotest.tools/v3/fs) -
32 32
   create temporary files and compare a filesystem tree to an expected value
33
-* [golden](http://gotest.tools/golden) -
33
+* [golden](http://pkg.go.dev/gotest.tools/v3/golden) -
34 34
   compare large multi-line strings against values frozen in golden files
35
-* [icmd](http://gotest.tools/icmd) -
35
+* [icmd](http://pkg.go.dev/gotest.tools/v3/icmd) -
36 36
   execute binaries and test the output
37
-* [poll](http://gotest.tools/poll) -
37
+* [poll](http://pkg.go.dev/gotest.tools/v3/poll) -
38 38
   test asynchronous code by polling until a desired state is reached
39
-* [skip](http://gotest.tools/skip) -
39
+* [skip](http://pkg.go.dev/gotest.tools/v3/skip) -
40 40
   skip a test and print the source code of the condition used to skip the test
41 41
 
42 42
 ## Related
... ...
@@ -49,7 +49,7 @@ The example below shows assert used with some common types.
49 49
 
50 50
 Comparisons
51 51
 
52
-Package http://gotest.tools/assert/cmp provides
52
+Package http://pkg.go.dev/gotest.tools/v3/assert/cmp provides
53 53
 many common comparisons. Additional comparisons can be written to compare
54 54
 values in other ways. See the example Assert (CustomComparison).
55 55
 
... ...
@@ -58,22 +58,16 @@ Automated migration from testify
58 58
 gty-migrate-from-testify is a command which translates Go source code from
59 59
 testify assertions to the assertions provided by this package.
60 60
 
61
-See http://gotest.tools/assert/cmd/gty-migrate-from-testify.
61
+See http://pkg.go.dev/gotest.tools/v3/assert/cmd/gty-migrate-from-testify.
62 62
 
63 63
 
64 64
 */
65 65
 package assert // import "gotest.tools/v3/assert"
66 66
 
67 67
 import (
68
-	"fmt"
69
-	"go/ast"
70
-	"go/token"
71
-	"reflect"
72
-
73 68
 	gocmp "github.com/google/go-cmp/cmp"
74 69
 	"gotest.tools/v3/assert/cmp"
75
-	"gotest.tools/v3/internal/format"
76
-	"gotest.tools/v3/internal/source"
70
+	"gotest.tools/v3/internal/assert"
77 71
 )
78 72
 
79 73
 // BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
... ...
@@ -90,128 +84,6 @@ type helperT interface {
90 90
 	Helper()
91 91
 }
92 92
 
93
-const failureMessage = "assertion failed: "
94
-
95
-// nolint: gocyclo
96
-func assert(
97
-	t TestingT,
98
-	failer func(),
99
-	argSelector argSelector,
100
-	comparison BoolOrComparison,
101
-	msgAndArgs ...interface{},
102
-) bool {
103
-	if ht, ok := t.(helperT); ok {
104
-		ht.Helper()
105
-	}
106
-	var success bool
107
-	switch check := comparison.(type) {
108
-	case bool:
109
-		if check {
110
-			return true
111
-		}
112
-		logFailureFromBool(t, msgAndArgs...)
113
-
114
-	// Undocumented legacy comparison without Result type
115
-	case func() (success bool, message string):
116
-		success = runCompareFunc(t, check, msgAndArgs...)
117
-
118
-	case nil:
119
-		return true
120
-
121
-	case error:
122
-		msg := failureMsgFromError(check)
123
-		t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
124
-
125
-	case cmp.Comparison:
126
-		success = runComparison(t, argSelector, check, msgAndArgs...)
127
-
128
-	case func() cmp.Result:
129
-		success = runComparison(t, argSelector, check, msgAndArgs...)
130
-
131
-	default:
132
-		t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
133
-	}
134
-
135
-	if success {
136
-		return true
137
-	}
138
-	failer()
139
-	return false
140
-}
141
-
142
-func runCompareFunc(
143
-	t TestingT,
144
-	f func() (success bool, message string),
145
-	msgAndArgs ...interface{},
146
-) bool {
147
-	if ht, ok := t.(helperT); ok {
148
-		ht.Helper()
149
-	}
150
-	if success, message := f(); !success {
151
-		t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
152
-		return false
153
-	}
154
-	return true
155
-}
156
-
157
-func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) {
158
-	if ht, ok := t.(helperT); ok {
159
-		ht.Helper()
160
-	}
161
-	const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool()
162
-	const comparisonArgPos = 1
163
-	args, err := source.CallExprArgs(stackIndex)
164
-	if err != nil {
165
-		t.Log(err.Error())
166
-		return
167
-	}
168
-
169
-	msg, err := boolFailureMessage(args[comparisonArgPos])
170
-	if err != nil {
171
-		t.Log(err.Error())
172
-		msg = "expression is false"
173
-	}
174
-
175
-	t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
176
-}
177
-
178
-func failureMsgFromError(err error) string {
179
-	// Handle errors with non-nil types
180
-	v := reflect.ValueOf(err)
181
-	if v.Kind() == reflect.Ptr && v.IsNil() {
182
-		return fmt.Sprintf("error is not nil: error has type %T", err)
183
-	}
184
-	return "error is not nil: " + err.Error()
185
-}
186
-
187
-func boolFailureMessage(expr ast.Expr) (string, error) {
188
-	if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ {
189
-		x, err := source.FormatNode(binaryExpr.X)
190
-		if err != nil {
191
-			return "", err
192
-		}
193
-		y, err := source.FormatNode(binaryExpr.Y)
194
-		if err != nil {
195
-			return "", err
196
-		}
197
-		return x + " is " + y, nil
198
-	}
199
-
200
-	if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT {
201
-		x, err := source.FormatNode(unaryExpr.X)
202
-		if err != nil {
203
-			return "", err
204
-		}
205
-		return x + " is true", nil
206
-	}
207
-
208
-	formatted, err := source.FormatNode(expr)
209
-	if err != nil {
210
-		return "", err
211
-	}
212
-	return "expression is false: " + formatted, nil
213
-}
214
-
215 93
 // Assert performs a comparison. If the comparison fails, the test is marked as
216 94
 // failed, a failure message is logged, and execution is stopped immediately.
217 95
 //
... ...
@@ -222,7 +94,7 @@ func boolFailureMessage(expr ast.Expr) (string, error) {
222 222
 //   cmp.Comparison
223 223
 // Uses cmp.Result.Success() to check for success of failure.
224 224
 // The comparison is responsible for producing a helpful failure message.
225
-// http://gotest.tools/assert/cmp provides many common comparisons.
225
+// http://pkg.go.dev/gotest.tools/v3/assert/cmp provides many common comparisons.
226 226
 //   error
227 227
 // A nil value is considered success.
228 228
 // A non-nil error is a failure, err.Error() is used as the failure message.
... ...
@@ -230,7 +102,9 @@ func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{})
230 230
 	if ht, ok := t.(helperT); ok {
231 231
 		ht.Helper()
232 232
 	}
233
-	assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...)
233
+	if !assert.Eval(t, assert.ArgsFromComparisonCall, comparison, msgAndArgs...) {
234
+		t.FailNow()
235
+	}
234 236
 }
235 237
 
236 238
 // Check performs a comparison. If the comparison fails the test is marked as
... ...
@@ -242,7 +116,11 @@ func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) b
242 242
 	if ht, ok := t.(helperT); ok {
243 243
 		ht.Helper()
244 244
 	}
245
-	return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...)
245
+	if !assert.Eval(t, assert.ArgsFromComparisonCall, comparison, msgAndArgs...) {
246
+		t.Fail()
247
+		return false
248
+	}
249
+	return true
246 250
 }
247 251
 
248 252
 // NilError fails the test immediately if err is not nil.
... ...
@@ -251,7 +129,9 @@ func NilError(t TestingT, err error, msgAndArgs ...interface{}) {
251 251
 	if ht, ok := t.(helperT); ok {
252 252
 		ht.Helper()
253 253
 	}
254
-	assert(t, t.FailNow, argsAfterT, err, msgAndArgs...)
254
+	if !assert.Eval(t, assert.ArgsAfterT, err, msgAndArgs...) {
255
+		t.FailNow()
256
+	}
255 257
 }
256 258
 
257 259
 // Equal uses the == operator to assert two values are equal and fails the test
... ...
@@ -270,13 +150,15 @@ func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
270 270
 	if ht, ok := t.(helperT); ok {
271 271
 		ht.Helper()
272 272
 	}
273
-	assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...)
273
+	if !assert.Eval(t, assert.ArgsAfterT, cmp.Equal(x, y), msgAndArgs...) {
274
+		t.FailNow()
275
+	}
274 276
 }
275 277
 
276 278
 // DeepEqual uses google/go-cmp (https://godoc.org/github.com/google/go-cmp/cmp)
277 279
 // to assert two values are equal and fails the test if they are not equal.
278 280
 //
279
-// Package http://gotest.tools/assert/opt provides some additional
281
+// Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional
280 282
 // commonly used Options.
281 283
 //
282 284
 // This is equivalent to Assert(t, cmp.DeepEqual(x, y)).
... ...
@@ -284,7 +166,9 @@ func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
284 284
 	if ht, ok := t.(helperT); ok {
285 285
 		ht.Helper()
286 286
 	}
287
-	assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...))
287
+	if !assert.Eval(t, assert.ArgsAfterT, cmp.DeepEqual(x, y, opts...)) {
288
+		t.FailNow()
289
+	}
288 290
 }
289 291
 
290 292
 // Error fails the test if err is nil, or the error message is not the expected
... ...
@@ -294,7 +178,9 @@ func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) {
294 294
 	if ht, ok := t.(helperT); ok {
295 295
 		ht.Helper()
296 296
 	}
297
-	assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...)
297
+	if !assert.Eval(t, assert.ArgsAfterT, cmp.Error(err, message), msgAndArgs...) {
298
+		t.FailNow()
299
+	}
298 300
 }
299 301
 
300 302
 // ErrorContains fails the test if err is nil, or the error message does not
... ...
@@ -304,7 +190,9 @@ func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interf
304 304
 	if ht, ok := t.(helperT); ok {
305 305
 		ht.Helper()
306 306
 	}
307
-	assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...)
307
+	if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...) {
308
+		t.FailNow()
309
+	}
308 310
 }
309 311
 
310 312
 // ErrorType fails the test if err is nil, or err is not the expected type.
... ...
@@ -325,5 +213,7 @@ func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interf
325 325
 	if ht, ok := t.(helperT); ok {
326 326
 		ht.Helper()
327 327
 	}
328
-	assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...)
328
+	if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorType(err, expected), msgAndArgs...) {
329
+		t.FailNow()
330
+	}
329 331
 }
... ...
@@ -21,7 +21,7 @@ type Comparison func() Result
21 21
 // and succeeds if the values are equal.
22 22
 //
23 23
 // The comparison can be customized using comparison Options.
24
-// Package http://gotest.tools/assert/opt provides some additional
24
+// Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional
25 25
 // commonly used Options.
26 26
 func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
27 27
 	return func() (result Result) {
... ...
@@ -52,13 +52,12 @@ func ResultFromError(err error) Result {
52 52
 }
53 53
 
54 54
 type templatedResult struct {
55
-	success  bool
56 55
 	template string
57 56
 	data     map[string]interface{}
58 57
 }
59 58
 
60 59
 func (r templatedResult) Success() bool {
61
-	return r.success
60
+	return false
62 61
 }
63 62
 
64 63
 func (r templatedResult) FailureMessage(args []ast.Expr) string {
65 64
deleted file mode 100644
... ...
@@ -1,106 +0,0 @@
1
-package assert
2
-
3
-import (
4
-	"fmt"
5
-	"go/ast"
6
-
7
-	"gotest.tools/v3/assert/cmp"
8
-	"gotest.tools/v3/internal/format"
9
-	"gotest.tools/v3/internal/source"
10
-)
11
-
12
-func runComparison(
13
-	t TestingT,
14
-	argSelector argSelector,
15
-	f cmp.Comparison,
16
-	msgAndArgs ...interface{},
17
-) bool {
18
-	if ht, ok := t.(helperT); ok {
19
-		ht.Helper()
20
-	}
21
-	result := f()
22
-	if result.Success() {
23
-		return true
24
-	}
25
-
26
-	var message string
27
-	switch typed := result.(type) {
28
-	case resultWithComparisonArgs:
29
-		const stackIndex = 3 // Assert/Check, assert, runComparison
30
-		args, err := source.CallExprArgs(stackIndex)
31
-		if err != nil {
32
-			t.Log(err.Error())
33
-		}
34
-		message = typed.FailureMessage(filterPrintableExpr(argSelector(args)))
35
-	case resultBasic:
36
-		message = typed.FailureMessage()
37
-	default:
38
-		message = fmt.Sprintf("comparison returned invalid Result type: %T", result)
39
-	}
40
-
41
-	t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
42
-	return false
43
-}
44
-
45
-type resultWithComparisonArgs interface {
46
-	FailureMessage(args []ast.Expr) string
47
-}
48
-
49
-type resultBasic interface {
50
-	FailureMessage() string
51
-}
52
-
53
-// filterPrintableExpr filters the ast.Expr slice to only include Expr that are
54
-// easy to read when printed and contain relevant information to an assertion.
55
-//
56
-// Ident and SelectorExpr are included because they print nicely and the variable
57
-// names may provide additional context to their values.
58
-// BasicLit and CompositeLit are excluded because their source is equivalent to
59
-// their value, which is already available.
60
-// Other types are ignored for now, but could be added if they are relevant.
61
-func filterPrintableExpr(args []ast.Expr) []ast.Expr {
62
-	result := make([]ast.Expr, len(args))
63
-	for i, arg := range args {
64
-		if isShortPrintableExpr(arg) {
65
-			result[i] = arg
66
-			continue
67
-		}
68
-
69
-		if starExpr, ok := arg.(*ast.StarExpr); ok {
70
-			result[i] = starExpr.X
71
-			continue
72
-		}
73
-	}
74
-	return result
75
-}
76
-
77
-func isShortPrintableExpr(expr ast.Expr) bool {
78
-	switch expr.(type) {
79
-	case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr:
80
-		return true
81
-	case *ast.BinaryExpr, *ast.UnaryExpr:
82
-		return true
83
-	default:
84
-		// CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr
85
-		return false
86
-	}
87
-}
88
-
89
-type argSelector func([]ast.Expr) []ast.Expr
90
-
91
-func argsAfterT(args []ast.Expr) []ast.Expr {
92
-	if len(args) < 1 {
93
-		return nil
94
-	}
95
-	return args[1:]
96
-}
97
-
98
-func argsFromComparisonCall(args []ast.Expr) []ast.Expr {
99
-	if len(args) < 1 {
100
-		return nil
101
-	}
102
-	if callExpr, ok := args[1].(*ast.CallExpr); ok {
103
-		return callExpr.Args
104
-	}
105
-	return nil
106
-}
... ...
@@ -1,7 +1,7 @@
1 1
 module gotest.tools/v3
2 2
 
3 3
 require (
4
-	github.com/google/go-cmp v0.3.0
4
+	github.com/google/go-cmp v0.4.0
5 5
 	github.com/pkg/errors v0.8.1
6 6
 	github.com/spf13/pflag v1.0.3
7 7
 	golang.org/x/tools v0.0.0-20190624222133-a101b041ded4
8 8
new file mode 100644
... ...
@@ -0,0 +1,143 @@
0
+package assert
1
+
2
+import (
3
+	"fmt"
4
+	"go/ast"
5
+	"go/token"
6
+	"reflect"
7
+
8
+	"gotest.tools/v3/assert/cmp"
9
+	"gotest.tools/v3/internal/format"
10
+	"gotest.tools/v3/internal/source"
11
+)
12
+
13
+// LogT is the subset of testing.T used by the assert package.
14
+type LogT interface {
15
+	Log(args ...interface{})
16
+}
17
+
18
+type helperT interface {
19
+	Helper()
20
+}
21
+
22
+const failureMessage = "assertion failed: "
23
+
24
+// Eval the comparison and print a failure messages if the comparison has failed.
25
+// nolint: gocyclo
26
+func Eval(
27
+	t LogT,
28
+	argSelector argSelector,
29
+	comparison interface{},
30
+	msgAndArgs ...interface{},
31
+) bool {
32
+	if ht, ok := t.(helperT); ok {
33
+		ht.Helper()
34
+	}
35
+	var success bool
36
+	switch check := comparison.(type) {
37
+	case bool:
38
+		if check {
39
+			return true
40
+		}
41
+		logFailureFromBool(t, msgAndArgs...)
42
+
43
+	// Undocumented legacy comparison without Result type
44
+	case func() (success bool, message string):
45
+		success = runCompareFunc(t, check, msgAndArgs...)
46
+
47
+	case nil:
48
+		return true
49
+
50
+	case error:
51
+		msg := failureMsgFromError(check)
52
+		t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
53
+
54
+	case cmp.Comparison:
55
+		success = RunComparison(t, argSelector, check, msgAndArgs...)
56
+
57
+	case func() cmp.Result:
58
+		success = RunComparison(t, argSelector, check, msgAndArgs...)
59
+
60
+	default:
61
+		t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
62
+	}
63
+	return success
64
+}
65
+
66
+func runCompareFunc(
67
+	t LogT,
68
+	f func() (success bool, message string),
69
+	msgAndArgs ...interface{},
70
+) bool {
71
+	if ht, ok := t.(helperT); ok {
72
+		ht.Helper()
73
+	}
74
+	if success, message := f(); !success {
75
+		t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
76
+		return false
77
+	}
78
+	return true
79
+}
80
+
81
+func logFailureFromBool(t LogT, msgAndArgs ...interface{}) {
82
+	if ht, ok := t.(helperT); ok {
83
+		ht.Helper()
84
+	}
85
+	const stackIndex = 3 // Assert()/Check(), assert(), logFailureFromBool()
86
+	args, err := source.CallExprArgs(stackIndex)
87
+	if err != nil {
88
+		t.Log(err.Error())
89
+		return
90
+	}
91
+
92
+	const comparisonArgIndex = 1 // Assert(t, comparison)
93
+	if len(args) <= comparisonArgIndex {
94
+		t.Log(failureMessage + "but assert failed to find the expression to print")
95
+		return
96
+	}
97
+
98
+	msg, err := boolFailureMessage(args[comparisonArgIndex])
99
+	if err != nil {
100
+		t.Log(err.Error())
101
+		msg = "expression is false"
102
+	}
103
+
104
+	t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
105
+}
106
+
107
+func failureMsgFromError(err error) string {
108
+	// Handle errors with non-nil types
109
+	v := reflect.ValueOf(err)
110
+	if v.Kind() == reflect.Ptr && v.IsNil() {
111
+		return fmt.Sprintf("error is not nil: error has type %T", err)
112
+	}
113
+	return "error is not nil: " + err.Error()
114
+}
115
+
116
+func boolFailureMessage(expr ast.Expr) (string, error) {
117
+	if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ {
118
+		x, err := source.FormatNode(binaryExpr.X)
119
+		if err != nil {
120
+			return "", err
121
+		}
122
+		y, err := source.FormatNode(binaryExpr.Y)
123
+		if err != nil {
124
+			return "", err
125
+		}
126
+		return x + " is " + y, nil
127
+	}
128
+
129
+	if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT {
130
+		x, err := source.FormatNode(unaryExpr.X)
131
+		if err != nil {
132
+			return "", err
133
+		}
134
+		return x + " is true", nil
135
+	}
136
+
137
+	formatted, err := source.FormatNode(expr)
138
+	if err != nil {
139
+		return "", err
140
+	}
141
+	return "expression is false: " + formatted, nil
142
+}
0 143
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+package assert
1
+
2
+import (
3
+	"fmt"
4
+	"go/ast"
5
+
6
+	"gotest.tools/v3/assert/cmp"
7
+	"gotest.tools/v3/internal/format"
8
+	"gotest.tools/v3/internal/source"
9
+)
10
+
11
+// RunComparison and return Comparison.Success. If the comparison fails a messages
12
+// will be printed using t.Log.
13
+func RunComparison(
14
+	t LogT,
15
+	argSelector argSelector,
16
+	f cmp.Comparison,
17
+	msgAndArgs ...interface{},
18
+) bool {
19
+	if ht, ok := t.(helperT); ok {
20
+		ht.Helper()
21
+	}
22
+	result := f()
23
+	if result.Success() {
24
+		return true
25
+	}
26
+
27
+	var message string
28
+	switch typed := result.(type) {
29
+	case resultWithComparisonArgs:
30
+		const stackIndex = 3 // Assert/Check, assert, RunComparison
31
+		args, err := source.CallExprArgs(stackIndex)
32
+		if err != nil {
33
+			t.Log(err.Error())
34
+		}
35
+		message = typed.FailureMessage(filterPrintableExpr(argSelector(args)))
36
+	case resultBasic:
37
+		message = typed.FailureMessage()
38
+	default:
39
+		message = fmt.Sprintf("comparison returned invalid Result type: %T", result)
40
+	}
41
+
42
+	t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
43
+	return false
44
+}
45
+
46
+type resultWithComparisonArgs interface {
47
+	FailureMessage(args []ast.Expr) string
48
+}
49
+
50
+type resultBasic interface {
51
+	FailureMessage() string
52
+}
53
+
54
+// filterPrintableExpr filters the ast.Expr slice to only include Expr that are
55
+// easy to read when printed and contain relevant information to an assertion.
56
+//
57
+// Ident and SelectorExpr are included because they print nicely and the variable
58
+// names may provide additional context to their values.
59
+// BasicLit and CompositeLit are excluded because their source is equivalent to
60
+// their value, which is already available.
61
+// Other types are ignored for now, but could be added if they are relevant.
62
+func filterPrintableExpr(args []ast.Expr) []ast.Expr {
63
+	result := make([]ast.Expr, len(args))
64
+	for i, arg := range args {
65
+		if isShortPrintableExpr(arg) {
66
+			result[i] = arg
67
+			continue
68
+		}
69
+
70
+		if starExpr, ok := arg.(*ast.StarExpr); ok {
71
+			result[i] = starExpr.X
72
+			continue
73
+		}
74
+	}
75
+	return result
76
+}
77
+
78
+func isShortPrintableExpr(expr ast.Expr) bool {
79
+	switch expr.(type) {
80
+	case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr:
81
+		return true
82
+	case *ast.BinaryExpr, *ast.UnaryExpr:
83
+		return true
84
+	default:
85
+		// CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr
86
+		return false
87
+	}
88
+}
89
+
90
+type argSelector func([]ast.Expr) []ast.Expr
91
+
92
+// ArgsAfterT selects args starting at position 1. Used when the caller has a
93
+// testing.T as the first argument, and the args to select should follow it.
94
+func ArgsAfterT(args []ast.Expr) []ast.Expr {
95
+	if len(args) < 1 {
96
+		return nil
97
+	}
98
+	return args[1:]
99
+}
100
+
101
+// ArgsFromComparisonCall selects args from the CallExpression at position 1.
102
+// Used when the caller has a testing.T as the first argument, and the args to
103
+// select are passed to the cmp.Comparison at position 1.
104
+func ArgsFromComparisonCall(args []ast.Expr) []ast.Expr {
105
+	if len(args) <= 1 {
106
+		return nil
107
+	}
108
+	if callExpr, ok := args[1].(*ast.CallExpr); ok {
109
+		return callExpr.Args
110
+	}
111
+	return nil
112
+}
113
+
114
+// ArgsAtZeroIndex selects args from the CallExpression at position 1.
115
+// Used when the caller accepts a single cmp.Comparison argument.
116
+func ArgsAtZeroIndex(args []ast.Expr) []ast.Expr {
117
+	if len(args) == 0 {
118
+		return nil
119
+	}
120
+	if callExpr, ok := args[0].(*ast.CallExpr); ok {
121
+		return callExpr.Args
122
+	}
123
+	return nil
124
+}
... ...
@@ -6,14 +6,17 @@ package cleanup
6 6
 import (
7 7
 	"os"
8 8
 	"strings"
9
-
10
-	"gotest.tools/v3/x/subtest"
11 9
 )
12 10
 
13 11
 type cleanupT interface {
14 12
 	Cleanup(f func())
15 13
 }
16 14
 
15
+// implemented by gotest.tools/x/subtest.TestContext
16
+type addCleanupT interface {
17
+	AddCleanup(f func())
18
+}
19
+
17 20
 type logT interface {
18 21
 	Log(...interface{})
19 22
 }
... ...
@@ -39,7 +42,7 @@ func Cleanup(t logT, f func()) {
39 39
 		ct.Cleanup(f)
40 40
 		return
41 41
 	}
42
-	if tc, ok := t.(subtest.TestContext); ok {
42
+	if tc, ok := t.(addCleanupT); ok {
43 43
 		tc.AddCleanup(f)
44 44
 	}
45 45
 }
... ...
@@ -93,8 +93,9 @@ func nodePosition(fileset *token.FileSet, node ast.Node) token.Position {
93 93
 }
94 94
 
95 95
 // GoVersionLessThan returns true if runtime.Version() is semantically less than
96
-// version 1.minor.
97
-func GoVersionLessThan(minor int64) bool {
96
+// version major.minor. Returns false if a release version can not be parsed from
97
+// runtime.Version().
98
+func GoVersionLessThan(major, minor int64) bool {
98 99
 	version := runtime.Version()
99 100
 	// not a release version
100 101
 	if !strings.HasPrefix(version, "go") {
... ...
@@ -105,11 +106,21 @@ func GoVersionLessThan(minor int64) bool {
105 105
 	if len(parts) < 2 {
106 106
 		return false
107 107
 	}
108
-	actual, err := strconv.ParseInt(parts[1], 10, 32)
109
-	return err == nil && parts[0] == "1" && actual < minor
108
+	rMajor, err := strconv.ParseInt(parts[0], 10, 32)
109
+	if err != nil {
110
+		return false
111
+	}
112
+	if rMajor != major {
113
+		return rMajor < major
114
+	}
115
+	rMinor, err := strconv.ParseInt(parts[1], 10, 32)
116
+	if err != nil {
117
+		return false
118
+	}
119
+	return rMinor < minor
110 120
 }
111 121
 
112
-var goVersionBefore19 = GoVersionLessThan(9)
122
+var goVersionBefore19 = GoVersionLessThan(1, 9)
113 123
 
114 124
 func getCallExprArgs(node ast.Node) ([]ast.Expr, error) {
115 125
 	visitor := &callExprVisitor{}
... ...
@@ -4,7 +4,11 @@ package poll // import "gotest.tools/v3/poll"
4 4
 
5 5
 import (
6 6
 	"fmt"
7
+	"strings"
7 8
 	"time"
9
+
10
+	"gotest.tools/v3/assert/cmp"
11
+	"gotest.tools/v3/internal/assert"
8 12
 )
9 13
 
10 14
 // TestingT is the subset of testing.T used by WaitOn
... ...
@@ -138,3 +142,30 @@ func WaitOn(t TestingT, check Check, pollOps ...SettingOp) {
138 138
 		}
139 139
 	}
140 140
 }
141
+
142
+// Compare values using the cmp.Comparison. If the comparison fails return a
143
+// result which indicates to WaitOn that it should continue waiting.
144
+// If the comparison is successful then WaitOn stops polling.
145
+func Compare(compare cmp.Comparison) Result {
146
+	buf := new(logBuffer)
147
+	if assert.RunComparison(buf, assert.ArgsAtZeroIndex, compare) {
148
+		return Success()
149
+	}
150
+	return Continue(buf.String())
151
+}
152
+
153
+type logBuffer struct {
154
+	log [][]interface{}
155
+}
156
+
157
+func (c *logBuffer) Log(args ...interface{}) {
158
+	c.log = append(c.log, args)
159
+}
160
+
161
+func (c *logBuffer) String() string {
162
+	b := new(strings.Builder)
163
+	for _, item := range c.log {
164
+		b.WriteString(fmt.Sprint(item...) + " ")
165
+	}
166
+	return b.String()
167
+}
141 168
deleted file mode 100644
... ...
@@ -1,84 +0,0 @@
1
-/*Package subtest provides a TestContext to subtests which handles cleanup, and
2
-provides a testing.TB, and context.Context.
3
-
4
-This package was inspired by github.com/frankban/quicktest.
5
-*/
6
-package subtest // import "gotest.tools/v3/x/subtest"
7
-
8
-import (
9
-	"context"
10
-	"testing"
11
-)
12
-
13
-type testcase struct {
14
-	testing.TB
15
-	ctx          context.Context
16
-	cleanupFuncs []cleanupFunc
17
-}
18
-
19
-type cleanupFunc func()
20
-
21
-func (tc *testcase) Ctx() context.Context {
22
-	if tc.ctx == nil {
23
-		var cancel func()
24
-		tc.ctx, cancel = context.WithCancel(context.Background())
25
-		tc.AddCleanup(cancel)
26
-	}
27
-	return tc.ctx
28
-}
29
-
30
-// cleanup runs all cleanup functions. Functions are run in the opposite order
31
-// in which they were added. Cleanup is called automatically before Run exits.
32
-func (tc *testcase) cleanup() {
33
-	for _, f := range tc.cleanupFuncs {
34
-		// Defer all cleanup functions so they all run even if one calls
35
-		// t.FailNow() or panics. Deferring them also runs them in reverse order.
36
-		defer f()
37
-	}
38
-	tc.cleanupFuncs = nil
39
-}
40
-
41
-func (tc *testcase) AddCleanup(f func()) {
42
-	tc.cleanupFuncs = append(tc.cleanupFuncs, f)
43
-}
44
-
45
-func (tc *testcase) Parallel() {
46
-	tp, ok := tc.TB.(parallel)
47
-	if !ok {
48
-		panic("Parallel called with a testing.B")
49
-	}
50
-	tp.Parallel()
51
-}
52
-
53
-type parallel interface {
54
-	Parallel()
55
-}
56
-
57
-// Run a subtest. When subtest exits, every cleanup function added with
58
-// TestContext.AddCleanup will be run.
59
-func Run(t *testing.T, name string, subtest func(t TestContext)) bool {
60
-	return t.Run(name, func(t *testing.T) {
61
-		tc := &testcase{TB: t}
62
-		defer tc.cleanup()
63
-		subtest(tc)
64
-	})
65
-}
66
-
67
-// TestContext provides a testing.TB and a context.Context for a test case.
68
-type TestContext interface {
69
-	testing.TB
70
-	// AddCleanup function which will be run when before Run returns.
71
-	//
72
-	// Deprecated: Go 1.14+ now includes a testing.TB.Cleanup(func()) which
73
-	// should be used instead. AddCleanup will be removed in a future release.
74
-	AddCleanup(f func())
75
-	// Ctx returns a context for the test case. Multiple calls from the same subtest
76
-	// will return the same context. The context is cancelled when Run
77
-	// returns.
78
-	Ctx() context.Context
79
-	// Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does
80
-	// not implement Parallel.
81
-	Parallel()
82
-}
83
-
84
-var _ TestContext = &testcase{}