- 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>
... | ... |
@@ -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 |
-} |
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{} |