1/*Package assert provides assertions for comparing expected values to actual
2values. When an assertion fails a helpful error message is printed.
3
4Assert and Check
5
6Assert() and Check() both accept a Comparison, and fail the test when the
7comparison fails. The one difference is that Assert() will end the test execution
8immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()),
9return the value of the comparison, then proceed with the rest of the test case.
10
11Example usage
12
13The example below shows assert used with some common types.
14
15
16	import (
17	    "testing"
18
19	    "gotest.tools/assert"
20	    is "gotest.tools/assert/cmp"
21	)
22
23	func TestEverything(t *testing.T) {
24	    // booleans
25	    assert.Assert(t, ok)
26	    assert.Assert(t, !missing)
27
28	    // primitives
29	    assert.Equal(t, count, 1)
30	    assert.Equal(t, msg, "the message")
31	    assert.Assert(t, total != 10) // NotEqual
32
33	    // errors
34	    assert.NilError(t, closer.Close())
35	    assert.Error(t, err, "the exact error message")
36	    assert.ErrorContains(t, err, "includes this")
37	    assert.ErrorType(t, err, os.IsNotExist)
38
39	    // complex types
40	    assert.DeepEqual(t, result, myStruct{Name: "title"})
41	    assert.Assert(t, is.Len(items, 3))
42	    assert.Assert(t, len(sequence) != 0) // NotEmpty
43	    assert.Assert(t, is.Contains(mapping, "key"))
44
45	    // pointers and interface
46	    assert.Assert(t, is.Nil(ref))
47	    assert.Assert(t, ref != nil) // NotNil
48	}
49
50Comparisons
51
52Package https://godoc.org/gotest.tools/assert/cmp provides
53many common comparisons. Additional comparisons can be written to compare
54values in other ways. See the example Assert (CustomComparison).
55
56Automated migration from testify
57
58gty-migrate-from-testify is a binary which can update source code which uses
59testify assertions to use the assertions provided by this package.
60
61See http://bit.do/cmd-gty-migrate-from-testify.
62
63
64*/
65package assert // import "gotest.tools/assert"
66
67import (
68	"fmt"
69	"go/ast"
70	"go/token"
71
72	gocmp "github.com/google/go-cmp/cmp"
73	"gotest.tools/assert/cmp"
74	"gotest.tools/internal/format"
75	"gotest.tools/internal/source"
76)
77
78// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
79type BoolOrComparison interface{}
80
81// TestingT is the subset of testing.T used by the assert package.
82type TestingT interface {
83	FailNow()
84	Fail()
85	Log(args ...interface{})
86}
87
88type helperT interface {
89	Helper()
90}
91
92const failureMessage = "assertion failed: "
93
94// nolint: gocyclo
95func assert(
96	t TestingT,
97	failer func(),
98	argSelector argSelector,
99	comparison BoolOrComparison,
100	msgAndArgs ...interface{},
101) bool {
102	if ht, ok := t.(helperT); ok {
103		ht.Helper()
104	}
105	var success bool
106	switch check := comparison.(type) {
107	case bool:
108		if check {
109			return true
110		}
111		logFailureFromBool(t, msgAndArgs...)
112
113	// Undocumented legacy comparison without Result type
114	case func() (success bool, message string):
115		success = runCompareFunc(t, check, msgAndArgs...)
116
117	case nil:
118		return true
119
120	case error:
121		msg := "error is not nil: "
122		t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...))
123
124	case cmp.Comparison:
125		success = runComparison(t, argSelector, check, msgAndArgs...)
126
127	case func() cmp.Result:
128		success = runComparison(t, argSelector, check, msgAndArgs...)
129
130	default:
131		t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check))
132	}
133
134	if success {
135		return true
136	}
137	failer()
138	return false
139}
140
141func runCompareFunc(
142	t TestingT,
143	f func() (success bool, message string),
144	msgAndArgs ...interface{},
145) bool {
146	if ht, ok := t.(helperT); ok {
147		ht.Helper()
148	}
149	if success, message := f(); !success {
150		t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...))
151		return false
152	}
153	return true
154}
155
156func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) {
157	if ht, ok := t.(helperT); ok {
158		ht.Helper()
159	}
160	const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool()
161	const comparisonArgPos = 1
162	args, err := source.CallExprArgs(stackIndex)
163	if err != nil {
164		t.Log(err.Error())
165		return
166	}
167
168	msg, err := boolFailureMessage(args[comparisonArgPos])
169	if err != nil {
170		t.Log(err.Error())
171		msg = "expression is false"
172	}
173
174	t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...))
175}
176
177func boolFailureMessage(expr ast.Expr) (string, error) {
178	if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ {
179		x, err := source.FormatNode(binaryExpr.X)
180		if err != nil {
181			return "", err
182		}
183		y, err := source.FormatNode(binaryExpr.Y)
184		if err != nil {
185			return "", err
186		}
187		return x + " is " + y, nil
188	}
189
190	if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT {
191		x, err := source.FormatNode(unaryExpr.X)
192		if err != nil {
193			return "", err
194		}
195		return x + " is true", nil
196	}
197
198	formatted, err := source.FormatNode(expr)
199	if err != nil {
200		return "", err
201	}
202	return "expression is false: " + formatted, nil
203}
204
205// Assert performs a comparison. If the comparison fails the test is marked as
206// failed, a failure message is logged, and execution is stopped immediately.
207//
208// The comparison argument may be one of three types: bool, cmp.Comparison or
209// error.
210// When called with a bool the failure message will contain the literal source
211// code of the expression.
212// When called with a cmp.Comparison the comparison is responsible for producing
213// a helpful failure message.
214// When called with an error a nil value is considered success. A non-nil error
215// is a failure, and Error() is used as the failure message.
216func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) {
217	if ht, ok := t.(helperT); ok {
218		ht.Helper()
219	}
220	assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...)
221}
222
223// Check performs a comparison. If the comparison fails the test is marked as
224// failed, a failure message is logged, and Check returns false. Otherwise returns
225// true.
226//
227// See Assert for details about the comparison arg and failure messages.
228func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool {
229	if ht, ok := t.(helperT); ok {
230		ht.Helper()
231	}
232	return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...)
233}
234
235// NilError fails the test immediately if err is not nil.
236// This is equivalent to Assert(t, err)
237func NilError(t TestingT, err error, msgAndArgs ...interface{}) {
238	if ht, ok := t.(helperT); ok {
239		ht.Helper()
240	}
241	assert(t, t.FailNow, argsAfterT, err, msgAndArgs...)
242}
243
244// Equal uses the == operator to assert two values are equal and fails the test
245// if they are not equal.
246//
247// If the comparison fails Equal will use the variable names for x and y as part
248// of the failure message to identify the actual and expected values.
249//
250// If either x or y are a multi-line string the failure message will include a
251// unified diff of the two values. If the values only differ by whitespace
252// the unified diff will be augmented by replacing whitespace characters with
253// visible characters to identify the whitespace difference.
254//
255// This is equivalent to Assert(t, cmp.Equal(x, y)).
256func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
257	if ht, ok := t.(helperT); ok {
258		ht.Helper()
259	}
260	assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...)
261}
262
263// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are
264// equal and fails the test if they are not equal.
265//
266// Package https://godoc.org/gotest.tools/assert/opt provides some additional
267// commonly used Options.
268//
269// This is equivalent to Assert(t, cmp.DeepEqual(x, y)).
270func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
271	if ht, ok := t.(helperT); ok {
272		ht.Helper()
273	}
274	assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...))
275}
276
277// Error fails the test if err is nil, or the error message is not the expected
278// message.
279// Equivalent to Assert(t, cmp.Error(err, message)).
280func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) {
281	if ht, ok := t.(helperT); ok {
282		ht.Helper()
283	}
284	assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...)
285}
286
287// ErrorContains fails the test if err is nil, or the error message does not
288// contain the expected substring.
289// Equivalent to Assert(t, cmp.ErrorContains(err, substring)).
290func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) {
291	if ht, ok := t.(helperT); ok {
292		ht.Helper()
293	}
294	assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...)
295}
296
297// ErrorType fails the test if err is nil, or err is not the expected type.
298//
299// Expected can be one of:
300// a func(error) bool which returns true if the error is the expected type,
301// an instance of (or a pointer to) a struct of the expected type,
302// a pointer to an interface the error is expected to implement,
303// a reflect.Type of the expected struct or interface.
304//
305// Equivalent to Assert(t, cmp.ErrorType(err, expected)).
306func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) {
307	if ht, ok := t.(helperT); ok {
308		ht.Helper()
309	}
310	assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...)
311}
312