blob: de8de0cb6c431ab7508a8d0dbba69e82c91c18df [file] [log] [blame]
khenaidooac637102019-01-14 15:44:34 -05001package assert
2
3import (
4 "bufio"
5 "bytes"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "math"
10 "os"
11 "reflect"
12 "regexp"
13 "runtime"
khenaidood948f772021-08-11 17:49:24 -040014 "runtime/debug"
khenaidooac637102019-01-14 15:44:34 -050015 "strings"
16 "time"
17 "unicode"
18 "unicode/utf8"
19
20 "github.com/davecgh/go-spew/spew"
21 "github.com/pmezard/go-difflib/difflib"
Abhay Kumara2ae5992025-11-10 14:02:24 +000022
23 // Wrapper around gopkg.in/yaml.v3
24 "github.com/stretchr/testify/assert/yaml"
khenaidooac637102019-01-14 15:44:34 -050025)
26
khenaidood948f772021-08-11 17:49:24 -040027//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
khenaidooac637102019-01-14 15:44:34 -050028
29// TestingT is an interface wrapper around *testing.T
30type TestingT interface {
31 Errorf(format string, args ...interface{})
32}
33
34// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
35// for table driven tests.
36type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool
37
38// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
39// for table driven tests.
40type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
41
42// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
43// for table driven tests.
44type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
45
46// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
47// for table driven tests.
48type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
49
Abhay Kumara2ae5992025-11-10 14:02:24 +000050// PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful
51// for table driven tests.
52type PanicAssertionFunc = func(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool
53
khenaidood948f772021-08-11 17:49:24 -040054// Comparison is a custom function that returns true on success and false on failure
khenaidooac637102019-01-14 15:44:34 -050055type Comparison func() (success bool)
56
57/*
58 Helper functions
59*/
60
61// ObjectsAreEqual determines if two objects are considered equal.
62//
63// This function does no assertion of any kind.
64func ObjectsAreEqual(expected, actual interface{}) bool {
65 if expected == nil || actual == nil {
66 return expected == actual
67 }
68
69 exp, ok := expected.([]byte)
70 if !ok {
71 return reflect.DeepEqual(expected, actual)
72 }
73
74 act, ok := actual.([]byte)
75 if !ok {
76 return false
77 }
78 if exp == nil || act == nil {
79 return exp == nil && act == nil
80 }
81 return bytes.Equal(exp, act)
82}
83
Abhay Kumara2ae5992025-11-10 14:02:24 +000084// copyExportedFields iterates downward through nested data structures and creates a copy
85// that only contains the exported struct fields.
86func copyExportedFields(expected interface{}) interface{} {
87 if isNil(expected) {
88 return expected
89 }
90
91 expectedType := reflect.TypeOf(expected)
92 expectedKind := expectedType.Kind()
93 expectedValue := reflect.ValueOf(expected)
94
95 switch expectedKind {
96 case reflect.Struct:
97 result := reflect.New(expectedType).Elem()
98 for i := 0; i < expectedType.NumField(); i++ {
99 field := expectedType.Field(i)
100 isExported := field.IsExported()
101 if isExported {
102 fieldValue := expectedValue.Field(i)
103 if isNil(fieldValue) || isNil(fieldValue.Interface()) {
104 continue
105 }
106 newValue := copyExportedFields(fieldValue.Interface())
107 result.Field(i).Set(reflect.ValueOf(newValue))
108 }
109 }
110 return result.Interface()
111
112 case reflect.Ptr:
113 result := reflect.New(expectedType.Elem())
114 unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface())
115 result.Elem().Set(reflect.ValueOf(unexportedRemoved))
116 return result.Interface()
117
118 case reflect.Array, reflect.Slice:
119 var result reflect.Value
120 if expectedKind == reflect.Array {
121 result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem()
122 } else {
123 result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
124 }
125 for i := 0; i < expectedValue.Len(); i++ {
126 index := expectedValue.Index(i)
127 if isNil(index) {
128 continue
129 }
130 unexportedRemoved := copyExportedFields(index.Interface())
131 result.Index(i).Set(reflect.ValueOf(unexportedRemoved))
132 }
133 return result.Interface()
134
135 case reflect.Map:
136 result := reflect.MakeMap(expectedType)
137 for _, k := range expectedValue.MapKeys() {
138 index := expectedValue.MapIndex(k)
139 unexportedRemoved := copyExportedFields(index.Interface())
140 result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved))
141 }
142 return result.Interface()
143
144 default:
145 return expected
146 }
147}
148
149// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are
150// considered equal. This comparison of only exported fields is applied recursively to nested data
151// structures.
152//
153// This function does no assertion of any kind.
154//
155// Deprecated: Use [EqualExportedValues] instead.
156func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
157 expectedCleaned := copyExportedFields(expected)
158 actualCleaned := copyExportedFields(actual)
159 return ObjectsAreEqualValues(expectedCleaned, actualCleaned)
160}
161
khenaidooac637102019-01-14 15:44:34 -0500162// ObjectsAreEqualValues gets whether two objects are equal, or if their
163// values are equal.
164func ObjectsAreEqualValues(expected, actual interface{}) bool {
165 if ObjectsAreEqual(expected, actual) {
166 return true
167 }
168
Abhay Kumara2ae5992025-11-10 14:02:24 +0000169 expectedValue := reflect.ValueOf(expected)
170 actualValue := reflect.ValueOf(actual)
171 if !expectedValue.IsValid() || !actualValue.IsValid() {
khenaidooac637102019-01-14 15:44:34 -0500172 return false
173 }
Abhay Kumara2ae5992025-11-10 14:02:24 +0000174
175 expectedType := expectedValue.Type()
176 actualType := actualValue.Type()
177 if !expectedType.ConvertibleTo(actualType) {
178 return false
khenaidooac637102019-01-14 15:44:34 -0500179 }
180
Abhay Kumara2ae5992025-11-10 14:02:24 +0000181 if !isNumericType(expectedType) || !isNumericType(actualType) {
182 // Attempt comparison after type conversion
183 return reflect.DeepEqual(
184 expectedValue.Convert(actualType).Interface(), actual,
185 )
186 }
187
188 // If BOTH values are numeric, there are chances of false positives due
189 // to overflow or underflow. So, we need to make sure to always convert
190 // the smaller type to a larger type before comparing.
191 if expectedType.Size() >= actualType.Size() {
192 return actualValue.Convert(expectedType).Interface() == expected
193 }
194
195 return expectedValue.Convert(actualType).Interface() == actual
196}
197
198// isNumericType returns true if the type is one of:
199// int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64,
200// float32, float64, complex64, complex128
201func isNumericType(t reflect.Type) bool {
202 return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128
khenaidooac637102019-01-14 15:44:34 -0500203}
204
205/* CallerInfo is necessary because the assert functions use the testing object
206internally, causing it to print the file:line of the assert method, rather than where
207the problem actually occurred in calling code.*/
208
209// CallerInfo returns an array of strings containing the file and line number
210// of each stack frame leading from the current test to the assert call that
211// failed.
212func CallerInfo() []string {
khenaidood948f772021-08-11 17:49:24 -0400213 var pc uintptr
khenaidood948f772021-08-11 17:49:24 -0400214 var file string
215 var line int
216 var name string
khenaidooac637102019-01-14 15:44:34 -0500217
Abhay Kumara2ae5992025-11-10 14:02:24 +0000218 const stackFrameBufferSize = 10
219 pcs := make([]uintptr, stackFrameBufferSize)
220
khenaidooac637102019-01-14 15:44:34 -0500221 callers := []string{}
Abhay Kumara2ae5992025-11-10 14:02:24 +0000222 offset := 1
223
224 for {
225 n := runtime.Callers(offset, pcs)
226
227 if n == 0 {
khenaidooac637102019-01-14 15:44:34 -0500228 break
229 }
230
Abhay Kumara2ae5992025-11-10 14:02:24 +0000231 frames := runtime.CallersFrames(pcs[:n])
khenaidooac637102019-01-14 15:44:34 -0500232
Abhay Kumara2ae5992025-11-10 14:02:24 +0000233 for {
234 frame, more := frames.Next()
235 pc = frame.PC
236 file = frame.File
237 line = frame.Line
khenaidooac637102019-01-14 15:44:34 -0500238
Abhay Kumara2ae5992025-11-10 14:02:24 +0000239 // This is a huge edge case, but it will panic if this is the case, see #180
240 if file == "<autogenerated>" {
241 break
242 }
khenaidooac637102019-01-14 15:44:34 -0500243
Abhay Kumara2ae5992025-11-10 14:02:24 +0000244 f := runtime.FuncForPC(pc)
245 if f == nil {
246 break
247 }
248 name = f.Name()
249
250 // testing.tRunner is the standard library function that calls
251 // tests. Subtests are called directly by tRunner, without going through
252 // the Test/Benchmark/Example function that contains the t.Run calls, so
253 // with subtests we should break when we hit tRunner, without adding it
254 // to the list of callers.
255 if name == "testing.tRunner" {
256 break
257 }
258
259 parts := strings.Split(file, "/")
260 if len(parts) > 1 {
261 filename := parts[len(parts)-1]
262 dir := parts[len(parts)-2]
263 if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
264 callers = append(callers, fmt.Sprintf("%s:%d", file, line))
265 }
266 }
267
268 // Drop the package
269 dotPos := strings.LastIndexByte(name, '.')
270 name = name[dotPos+1:]
271 if isTest(name, "Test") ||
272 isTest(name, "Benchmark") ||
273 isTest(name, "Example") {
274 break
275 }
276
277 if !more {
278 break
khenaidooac637102019-01-14 15:44:34 -0500279 }
280 }
281
Abhay Kumara2ae5992025-11-10 14:02:24 +0000282 // Next batch
283 offset += cap(pcs)
khenaidooac637102019-01-14 15:44:34 -0500284 }
285
286 return callers
287}
288
289// Stolen from the `go test` tool.
290// isTest tells whether name looks like a test (or benchmark, according to prefix).
291// It is a Test (say) if there is a character after Test that is not a lower-case letter.
292// We don't want TesticularCancer.
293func isTest(name, prefix string) bool {
294 if !strings.HasPrefix(name, prefix) {
295 return false
296 }
297 if len(name) == len(prefix) { // "Test" is ok
298 return true
299 }
khenaidood948f772021-08-11 17:49:24 -0400300 r, _ := utf8.DecodeRuneInString(name[len(prefix):])
301 return !unicode.IsLower(r)
khenaidooac637102019-01-14 15:44:34 -0500302}
303
304func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
305 if len(msgAndArgs) == 0 || msgAndArgs == nil {
306 return ""
307 }
308 if len(msgAndArgs) == 1 {
309 msg := msgAndArgs[0]
310 if msgAsStr, ok := msg.(string); ok {
311 return msgAsStr
312 }
313 return fmt.Sprintf("%+v", msg)
314 }
315 if len(msgAndArgs) > 1 {
316 return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
317 }
318 return ""
319}
320
321// Aligns the provided message so that all lines after the first line start at the same location as the first line.
322// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
Abhay Kumara2ae5992025-11-10 14:02:24 +0000323// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the
khenaidooac637102019-01-14 15:44:34 -0500324// basis on which the alignment occurs).
325func indentMessageLines(message string, longestLabelLen int) string {
326 outBuf := new(bytes.Buffer)
327
328 for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
329 // no need to align first line because it starts at the correct location (after the label)
330 if i != 0 {
331 // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
332 outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
333 }
334 outBuf.WriteString(scanner.Text())
335 }
336
337 return outBuf.String()
338}
339
340type failNower interface {
341 FailNow()
342}
343
344// FailNow fails test
345func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
346 if h, ok := t.(tHelper); ok {
347 h.Helper()
348 }
349 Fail(t, failureMessage, msgAndArgs...)
350
351 // We cannot extend TestingT with FailNow() and
352 // maintain backwards compatibility, so we fallback
353 // to panicking when FailNow is not available in
354 // TestingT.
355 // See issue #263
356
357 if t, ok := t.(failNower); ok {
358 t.FailNow()
359 } else {
360 panic("test failed and t is missing `FailNow()`")
361 }
362 return false
363}
364
365// Fail reports a failure through
366func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
367 if h, ok := t.(tHelper); ok {
368 h.Helper()
369 }
370 content := []labeledContent{
371 {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")},
372 {"Error", failureMessage},
373 }
374
375 // Add test name if the Go version supports it
376 if n, ok := t.(interface {
377 Name() string
378 }); ok {
379 content = append(content, labeledContent{"Test", n.Name()})
380 }
381
382 message := messageFromMsgAndArgs(msgAndArgs...)
383 if len(message) > 0 {
384 content = append(content, labeledContent{"Messages", message})
385 }
386
387 t.Errorf("\n%s", ""+labeledOutput(content...))
388
389 return false
390}
391
392type labeledContent struct {
393 label string
394 content string
395}
396
397// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:
398//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000399// \t{{label}}:{{align_spaces}}\t{{content}}\n
khenaidooac637102019-01-14 15:44:34 -0500400//
401// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label.
402// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this
403// alignment is achieved, "\t{{content}}\n" is added for the output.
404//
405// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line.
406func labeledOutput(content ...labeledContent) string {
407 longestLabel := 0
408 for _, v := range content {
409 if len(v.label) > longestLabel {
410 longestLabel = len(v.label)
411 }
412 }
413 var output string
414 for _, v := range content {
415 output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
416 }
417 return output
418}
419
420// Implements asserts that an object is implemented by the specified interface.
421//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000422// assert.Implements(t, (*MyInterface)(nil), new(MyObject))
khenaidooac637102019-01-14 15:44:34 -0500423func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
424 if h, ok := t.(tHelper); ok {
425 h.Helper()
426 }
427 interfaceType := reflect.TypeOf(interfaceObject).Elem()
428
429 if object == nil {
430 return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...)
431 }
432 if !reflect.TypeOf(object).Implements(interfaceType) {
433 return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...)
434 }
435
436 return true
437}
438
Abhay Kumara2ae5992025-11-10 14:02:24 +0000439// NotImplements asserts that an object does not implement the specified interface.
440//
441// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject))
442func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -0500443 if h, ok := t.(tHelper); ok {
444 h.Helper()
445 }
Abhay Kumara2ae5992025-11-10 14:02:24 +0000446 interfaceType := reflect.TypeOf(interfaceObject).Elem()
khenaidooac637102019-01-14 15:44:34 -0500447
Abhay Kumara2ae5992025-11-10 14:02:24 +0000448 if object == nil {
449 return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...)
450 }
451 if reflect.TypeOf(object).Implements(interfaceType) {
452 return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500453 }
454
455 return true
456}
457
Abhay Kumara2ae5992025-11-10 14:02:24 +0000458func isType(expectedType, object interface{}) bool {
459 return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType))
460}
461
462// IsType asserts that the specified objects are of the same type.
463//
464// assert.IsType(t, &MyStruct{}, &MyStruct{})
465func IsType(t TestingT, expectedType, object interface{}, msgAndArgs ...interface{}) bool {
466 if isType(expectedType, object) {
467 return true
468 }
469 if h, ok := t.(tHelper); ok {
470 h.Helper()
471 }
472 return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...)
473}
474
475// IsNotType asserts that the specified objects are not of the same type.
476//
477// assert.IsNotType(t, &NotMyStruct{}, &MyStruct{})
478func IsNotType(t TestingT, theType, object interface{}, msgAndArgs ...interface{}) bool {
479 if !isType(theType, object) {
480 return true
481 }
482 if h, ok := t.(tHelper); ok {
483 h.Helper()
484 }
485 return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...)
486}
487
khenaidooac637102019-01-14 15:44:34 -0500488// Equal asserts that two objects are equal.
489//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000490// assert.Equal(t, 123, 123)
khenaidooac637102019-01-14 15:44:34 -0500491//
492// Pointer variable equality is determined based on the equality of the
493// referenced values (as opposed to the memory addresses). Function equality
494// cannot be determined and will always fail.
495func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
496 if h, ok := t.(tHelper); ok {
497 h.Helper()
498 }
499 if err := validateEqualArgs(expected, actual); err != nil {
500 return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
501 expected, actual, err), msgAndArgs...)
502 }
503
504 if !ObjectsAreEqual(expected, actual) {
505 diff := diff(expected, actual)
506 expected, actual = formatUnequalValues(expected, actual)
507 return Fail(t, fmt.Sprintf("Not equal: \n"+
508 "expected: %s\n"+
509 "actual : %s%s", expected, actual, diff), msgAndArgs...)
510 }
511
512 return true
khenaidooac637102019-01-14 15:44:34 -0500513}
514
khenaidood948f772021-08-11 17:49:24 -0400515// validateEqualArgs checks whether provided arguments can be safely used in the
516// Equal/NotEqual functions.
517func validateEqualArgs(expected, actual interface{}) error {
518 if expected == nil && actual == nil {
519 return nil
520 }
521
522 if isFunction(expected) || isFunction(actual) {
523 return errors.New("cannot take func type as argument")
524 }
525 return nil
526}
527
Scott Baker8461e152019-10-01 14:44:30 -0700528// Same asserts that two pointers reference the same object.
529//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000530// assert.Same(t, ptr1, ptr2)
Scott Baker8461e152019-10-01 14:44:30 -0700531//
532// Both arguments must be pointer variables. Pointer variable sameness is
533// determined based on the equality of both type and value.
534func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
535 if h, ok := t.(tHelper); ok {
536 h.Helper()
537 }
538
Abhay Kumara2ae5992025-11-10 14:02:24 +0000539 same, ok := samePointers(expected, actual)
540 if !ok {
541 return Fail(t, "Both arguments must be pointers", msgAndArgs...)
542 }
543
544 if !same {
545 // both are pointers but not the same type & pointing to the same address
Scott Baker8461e152019-10-01 14:44:30 -0700546 return Fail(t, fmt.Sprintf("Not same: \n"+
Abhay Kumara2ae5992025-11-10 14:02:24 +0000547 "expected: %p %#[1]v\n"+
548 "actual : %p %#[2]v",
549 expected, actual), msgAndArgs...)
Scott Baker8461e152019-10-01 14:44:30 -0700550 }
551
552 return true
553}
554
khenaidood948f772021-08-11 17:49:24 -0400555// NotSame asserts that two pointers do not reference the same object.
556//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000557// assert.NotSame(t, ptr1, ptr2)
khenaidood948f772021-08-11 17:49:24 -0400558//
559// Both arguments must be pointer variables. Pointer variable sameness is
560// determined based on the equality of both type and value.
561func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
562 if h, ok := t.(tHelper); ok {
563 h.Helper()
564 }
565
Abhay Kumara2ae5992025-11-10 14:02:24 +0000566 same, ok := samePointers(expected, actual)
567 if !ok {
568 // fails when the arguments are not pointers
569 return !(Fail(t, "Both arguments must be pointers", msgAndArgs...))
570 }
571
572 if same {
khenaidood948f772021-08-11 17:49:24 -0400573 return Fail(t, fmt.Sprintf(
Abhay Kumara2ae5992025-11-10 14:02:24 +0000574 "Expected and actual point to the same object: %p %#[1]v",
575 expected), msgAndArgs...)
khenaidood948f772021-08-11 17:49:24 -0400576 }
577 return true
578}
579
Abhay Kumara2ae5992025-11-10 14:02:24 +0000580// samePointers checks if two generic interface objects are pointers of the same
581// type pointing to the same object. It returns two values: same indicating if
582// they are the same type and point to the same object, and ok indicating that
583// both inputs are pointers.
584func samePointers(first, second interface{}) (same bool, ok bool) {
khenaidood948f772021-08-11 17:49:24 -0400585 firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second)
586 if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000587 return false, false // not both are pointers
khenaidood948f772021-08-11 17:49:24 -0400588 }
589
590 firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second)
591 if firstType != secondType {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000592 return false, true // both are pointers, but of different types
khenaidood948f772021-08-11 17:49:24 -0400593 }
594
595 // compare pointer addresses
Abhay Kumara2ae5992025-11-10 14:02:24 +0000596 return first == second, true
khenaidood948f772021-08-11 17:49:24 -0400597}
598
khenaidooac637102019-01-14 15:44:34 -0500599// formatUnequalValues takes two values of arbitrary types and returns string
600// representations appropriate to be presented to the user.
601//
602// If the values are not of like type, the returned strings will be prefixed
Abhay Kumara2ae5992025-11-10 14:02:24 +0000603// with the type name, and the value will be enclosed in parentheses similar
khenaidooac637102019-01-14 15:44:34 -0500604// to a type conversion in the Go grammar.
605func formatUnequalValues(expected, actual interface{}) (e string, a string) {
606 if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
khenaidood948f772021-08-11 17:49:24 -0400607 return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)),
608 fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual))
khenaidooac637102019-01-14 15:44:34 -0500609 }
khenaidood948f772021-08-11 17:49:24 -0400610 switch expected.(type) {
611 case time.Duration:
612 return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
613 }
614 return truncatingFormat(expected), truncatingFormat(actual)
615}
khenaidooac637102019-01-14 15:44:34 -0500616
khenaidood948f772021-08-11 17:49:24 -0400617// truncatingFormat formats the data and truncates it if it's too long.
618//
619// This helps keep formatted error messages lines from exceeding the
620// bufio.MaxScanTokenSize max line length that the go testing framework imposes.
621func truncatingFormat(data interface{}) string {
622 value := fmt.Sprintf("%#v", data)
623 max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed.
624 if len(value) > max {
625 value = value[0:max] + "<... truncated>"
626 }
627 return value
khenaidooac637102019-01-14 15:44:34 -0500628}
629
Abhay Kumara2ae5992025-11-10 14:02:24 +0000630// EqualValues asserts that two objects are equal or convertible to the larger
631// type and equal.
khenaidooac637102019-01-14 15:44:34 -0500632//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000633// assert.EqualValues(t, uint32(123), int32(123))
khenaidooac637102019-01-14 15:44:34 -0500634func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
635 if h, ok := t.(tHelper); ok {
636 h.Helper()
637 }
638
639 if !ObjectsAreEqualValues(expected, actual) {
640 diff := diff(expected, actual)
641 expected, actual = formatUnequalValues(expected, actual)
642 return Fail(t, fmt.Sprintf("Not equal: \n"+
643 "expected: %s\n"+
644 "actual : %s%s", expected, actual, diff), msgAndArgs...)
645 }
646
647 return true
Abhay Kumara2ae5992025-11-10 14:02:24 +0000648}
khenaidooac637102019-01-14 15:44:34 -0500649
Abhay Kumara2ae5992025-11-10 14:02:24 +0000650// EqualExportedValues asserts that the types of two objects are equal and their public
651// fields are also equal. This is useful for comparing structs that have private fields
652// that could potentially differ.
653//
654// type S struct {
655// Exported int
656// notExported int
657// }
658// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
659// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
660func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
661 if h, ok := t.(tHelper); ok {
662 h.Helper()
663 }
664
665 aType := reflect.TypeOf(expected)
666 bType := reflect.TypeOf(actual)
667
668 if aType != bType {
669 return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
670 }
671
672 expected = copyExportedFields(expected)
673 actual = copyExportedFields(actual)
674
675 if !ObjectsAreEqualValues(expected, actual) {
676 diff := diff(expected, actual)
677 expected, actual = formatUnequalValues(expected, actual)
678 return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+
679 "expected: %s\n"+
680 "actual : %s%s", expected, actual, diff), msgAndArgs...)
681 }
682
683 return true
khenaidooac637102019-01-14 15:44:34 -0500684}
685
686// Exactly asserts that two objects are equal in value and type.
687//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000688// assert.Exactly(t, int32(123), int64(123))
khenaidooac637102019-01-14 15:44:34 -0500689func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
690 if h, ok := t.(tHelper); ok {
691 h.Helper()
692 }
693
694 aType := reflect.TypeOf(expected)
695 bType := reflect.TypeOf(actual)
696
697 if aType != bType {
698 return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
699 }
700
701 return Equal(t, expected, actual, msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500702}
703
704// NotNil asserts that the specified object is not nil.
705//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000706// assert.NotNil(t, err)
khenaidooac637102019-01-14 15:44:34 -0500707func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -0500708 if !isNil(object) {
709 return true
710 }
khenaidood948f772021-08-11 17:49:24 -0400711 if h, ok := t.(tHelper); ok {
712 h.Helper()
713 }
khenaidooac637102019-01-14 15:44:34 -0500714 return Fail(t, "Expected value not to be nil.", msgAndArgs...)
715}
716
khenaidooac637102019-01-14 15:44:34 -0500717// isNil checks if a specified object is nil or not, without Failing.
718func isNil(object interface{}) bool {
719 if object == nil {
720 return true
721 }
722
723 value := reflect.ValueOf(object)
Abhay Kumara2ae5992025-11-10 14:02:24 +0000724 switch value.Kind() {
725 case
726 reflect.Chan, reflect.Func,
727 reflect.Interface, reflect.Map,
728 reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
khenaidooac637102019-01-14 15:44:34 -0500729
Abhay Kumara2ae5992025-11-10 14:02:24 +0000730 return value.IsNil()
khenaidooac637102019-01-14 15:44:34 -0500731 }
732
733 return false
734}
735
736// Nil asserts that the specified object is nil.
737//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000738// assert.Nil(t, err)
khenaidooac637102019-01-14 15:44:34 -0500739func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -0500740 if isNil(object) {
741 return true
742 }
khenaidood948f772021-08-11 17:49:24 -0400743 if h, ok := t.(tHelper); ok {
744 h.Helper()
745 }
khenaidooac637102019-01-14 15:44:34 -0500746 return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
747}
748
749// isEmpty gets whether the specified object is considered empty or not.
750func isEmpty(object interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -0500751 // get nil case out of the way
752 if object == nil {
753 return true
754 }
755
Abhay Kumara2ae5992025-11-10 14:02:24 +0000756 return isEmptyValue(reflect.ValueOf(object))
khenaidooac637102019-01-14 15:44:34 -0500757}
758
Abhay Kumara2ae5992025-11-10 14:02:24 +0000759// isEmptyValue gets whether the specified reflect.Value is considered empty or not.
760func isEmptyValue(objValue reflect.Value) bool {
761 if objValue.IsZero() {
762 return true
763 }
764 // Special cases of non-zero values that we consider empty
765 switch objValue.Kind() {
766 // collection types are empty when they have no element
767 // Note: array types are empty when they match their zero-initialized state.
768 case reflect.Chan, reflect.Map, reflect.Slice:
769 return objValue.Len() == 0
770 // non-nil pointers are empty if the value they point to is empty
771 case reflect.Ptr:
772 return isEmptyValue(objValue.Elem())
773 }
774 return false
775}
776
777// Empty asserts that the given value is "empty".
khenaidooac637102019-01-14 15:44:34 -0500778//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000779// [Zero values] are "empty".
780//
781// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
782//
783// Slices, maps and channels with zero length are "empty".
784//
785// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
786//
787// assert.Empty(t, obj)
788//
789// [Zero values]: https://go.dev/ref/spec#The_zero_value
khenaidooac637102019-01-14 15:44:34 -0500790func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -0500791 pass := isEmpty(object)
792 if !pass {
khenaidood948f772021-08-11 17:49:24 -0400793 if h, ok := t.(tHelper); ok {
794 h.Helper()
795 }
khenaidooac637102019-01-14 15:44:34 -0500796 Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
797 }
798
799 return pass
khenaidooac637102019-01-14 15:44:34 -0500800}
801
Abhay Kumara2ae5992025-11-10 14:02:24 +0000802// NotEmpty asserts that the specified object is NOT [Empty].
khenaidooac637102019-01-14 15:44:34 -0500803//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000804// if assert.NotEmpty(t, obj) {
805// assert.Equal(t, "two", obj[1])
806// }
khenaidooac637102019-01-14 15:44:34 -0500807func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -0500808 pass := !isEmpty(object)
809 if !pass {
khenaidood948f772021-08-11 17:49:24 -0400810 if h, ok := t.(tHelper); ok {
811 h.Helper()
812 }
khenaidooac637102019-01-14 15:44:34 -0500813 Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
814 }
815
816 return pass
khenaidooac637102019-01-14 15:44:34 -0500817}
818
Abhay Kumara2ae5992025-11-10 14:02:24 +0000819// getLen tries to get the length of an object.
820// It returns (0, false) if impossible.
821func getLen(x interface{}) (length int, ok bool) {
khenaidooac637102019-01-14 15:44:34 -0500822 v := reflect.ValueOf(x)
823 defer func() {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000824 ok = recover() == nil
khenaidooac637102019-01-14 15:44:34 -0500825 }()
Abhay Kumara2ae5992025-11-10 14:02:24 +0000826 return v.Len(), true
khenaidooac637102019-01-14 15:44:34 -0500827}
828
829// Len asserts that the specified object has specific length.
830// Len also fails if the object has a type that len() not accept.
831//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000832// assert.Len(t, mySlice, 3)
khenaidooac637102019-01-14 15:44:34 -0500833func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
834 if h, ok := t.(tHelper); ok {
835 h.Helper()
836 }
Abhay Kumara2ae5992025-11-10 14:02:24 +0000837 l, ok := getLen(object)
khenaidooac637102019-01-14 15:44:34 -0500838 if !ok {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000839 return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500840 }
841
842 if l != length {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000843 return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500844 }
845 return true
846}
847
848// True asserts that the specified value is true.
849//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000850// assert.True(t, myBool)
khenaidooac637102019-01-14 15:44:34 -0500851func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
khenaidood948f772021-08-11 17:49:24 -0400852 if !value {
853 if h, ok := t.(tHelper); ok {
854 h.Helper()
855 }
khenaidooac637102019-01-14 15:44:34 -0500856 return Fail(t, "Should be true", msgAndArgs...)
857 }
858
859 return true
khenaidooac637102019-01-14 15:44:34 -0500860}
861
862// False asserts that the specified value is false.
863//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000864// assert.False(t, myBool)
khenaidooac637102019-01-14 15:44:34 -0500865func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
khenaidood948f772021-08-11 17:49:24 -0400866 if value {
867 if h, ok := t.(tHelper); ok {
868 h.Helper()
869 }
khenaidooac637102019-01-14 15:44:34 -0500870 return Fail(t, "Should be false", msgAndArgs...)
871 }
872
873 return true
khenaidooac637102019-01-14 15:44:34 -0500874}
875
876// NotEqual asserts that the specified values are NOT equal.
877//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000878// assert.NotEqual(t, obj1, obj2)
khenaidooac637102019-01-14 15:44:34 -0500879//
880// Pointer variable equality is determined based on the equality of the
881// referenced values (as opposed to the memory addresses).
882func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
883 if h, ok := t.(tHelper); ok {
884 h.Helper()
885 }
886 if err := validateEqualArgs(expected, actual); err != nil {
887 return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
888 expected, actual, err), msgAndArgs...)
889 }
890
891 if ObjectsAreEqual(expected, actual) {
892 return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
893 }
894
895 return true
khenaidooac637102019-01-14 15:44:34 -0500896}
897
khenaidood948f772021-08-11 17:49:24 -0400898// NotEqualValues asserts that two objects are not equal even when converted to the same type
899//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000900// assert.NotEqualValues(t, obj1, obj2)
khenaidood948f772021-08-11 17:49:24 -0400901func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
902 if h, ok := t.(tHelper); ok {
903 h.Helper()
904 }
905
906 if ObjectsAreEqualValues(expected, actual) {
907 return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
908 }
909
910 return true
911}
912
khenaidooac637102019-01-14 15:44:34 -0500913// containsElement try loop over the list check if the list includes the element.
914// return (false, false) if impossible.
915// return (true, false) if element was not found.
916// return (true, true) if element was found.
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530917func containsElement(list interface{}, element interface{}) (ok, found bool) {
khenaidooac637102019-01-14 15:44:34 -0500918 listValue := reflect.ValueOf(list)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530919 listType := reflect.TypeOf(list)
920 if listType == nil {
921 return false, false
922 }
923 listKind := listType.Kind()
khenaidooac637102019-01-14 15:44:34 -0500924 defer func() {
925 if e := recover(); e != nil {
926 ok = false
927 found = false
928 }
929 }()
930
Scott Baker8461e152019-10-01 14:44:30 -0700931 if listKind == reflect.String {
932 elementValue := reflect.ValueOf(element)
khenaidooac637102019-01-14 15:44:34 -0500933 return true, strings.Contains(listValue.String(), elementValue.String())
934 }
935
Scott Baker8461e152019-10-01 14:44:30 -0700936 if listKind == reflect.Map {
khenaidooac637102019-01-14 15:44:34 -0500937 mapKeys := listValue.MapKeys()
938 for i := 0; i < len(mapKeys); i++ {
939 if ObjectsAreEqual(mapKeys[i].Interface(), element) {
940 return true, true
941 }
942 }
943 return true, false
944 }
945
946 for i := 0; i < listValue.Len(); i++ {
947 if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
948 return true, true
949 }
950 }
951 return true, false
khenaidooac637102019-01-14 15:44:34 -0500952}
953
954// Contains asserts that the specified string, list(array, slice...) or map contains the
955// specified substring or element.
956//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000957// assert.Contains(t, "Hello World", "World")
958// assert.Contains(t, ["Hello", "World"], "World")
959// assert.Contains(t, {"Hello": "World"}, "Hello")
khenaidooac637102019-01-14 15:44:34 -0500960func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
961 if h, ok := t.(tHelper); ok {
962 h.Helper()
963 }
964
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530965 ok, found := containsElement(s, contains)
khenaidooac637102019-01-14 15:44:34 -0500966 if !ok {
khenaidood948f772021-08-11 17:49:24 -0400967 return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500968 }
969 if !found {
khenaidood948f772021-08-11 17:49:24 -0400970 return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500971 }
972
973 return true
khenaidooac637102019-01-14 15:44:34 -0500974}
975
976// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
977// specified substring or element.
978//
Abhay Kumara2ae5992025-11-10 14:02:24 +0000979// assert.NotContains(t, "Hello World", "Earth")
980// assert.NotContains(t, ["Hello", "World"], "Earth")
981// assert.NotContains(t, {"Hello": "World"}, "Earth")
khenaidooac637102019-01-14 15:44:34 -0500982func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
983 if h, ok := t.(tHelper); ok {
984 h.Helper()
985 }
986
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +0530987 ok, found := containsElement(s, contains)
khenaidooac637102019-01-14 15:44:34 -0500988 if !ok {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000989 return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500990 }
991 if found {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000992 return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -0500993 }
994
995 return true
khenaidooac637102019-01-14 15:44:34 -0500996}
997
Abhay Kumara2ae5992025-11-10 14:02:24 +0000998// Subset asserts that the list (array, slice, or map) contains all elements
999// given in the subset (array, slice, or map).
1000// Map elements are key-value pairs unless compared with an array or slice where
1001// only the map key is evaluated.
khenaidooac637102019-01-14 15:44:34 -05001002//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001003// assert.Subset(t, [1, 2, 3], [1, 2])
1004// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1})
1005// assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"})
1006// assert.Subset(t, {"x": 1, "y": 2}, ["x"])
khenaidooac637102019-01-14 15:44:34 -05001007func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
1008 if h, ok := t.(tHelper); ok {
1009 h.Helper()
1010 }
1011 if subset == nil {
1012 return true // we consider nil to be equal to the nil set
1013 }
1014
khenaidooac637102019-01-14 15:44:34 -05001015 listKind := reflect.TypeOf(list).Kind()
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301016 if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
khenaidooac637102019-01-14 15:44:34 -05001017 return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
1018 }
1019
Abhay Kumara2ae5992025-11-10 14:02:24 +00001020 subsetKind := reflect.TypeOf(subset).Kind()
1021 if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
khenaidooac637102019-01-14 15:44:34 -05001022 return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
1023 }
1024
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301025 if subsetKind == reflect.Map && listKind == reflect.Map {
Abhay Kumara2ae5992025-11-10 14:02:24 +00001026 subsetMap := reflect.ValueOf(subset)
1027 actualMap := reflect.ValueOf(list)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301028
Abhay Kumara2ae5992025-11-10 14:02:24 +00001029 for _, k := range subsetMap.MapKeys() {
1030 ev := subsetMap.MapIndex(k)
1031 av := actualMap.MapIndex(k)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301032
Abhay Kumara2ae5992025-11-10 14:02:24 +00001033 if !av.IsValid() {
1034 return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
1035 }
1036 if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
1037 return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301038 }
1039 }
1040
1041 return true
1042 }
1043
Abhay Kumara2ae5992025-11-10 14:02:24 +00001044 subsetList := reflect.ValueOf(subset)
1045 if subsetKind == reflect.Map {
1046 keys := make([]interface{}, subsetList.Len())
1047 for idx, key := range subsetList.MapKeys() {
1048 keys[idx] = key.Interface()
1049 }
1050 subsetList = reflect.ValueOf(keys)
1051 }
1052 for i := 0; i < subsetList.Len(); i++ {
1053 element := subsetList.Index(i).Interface()
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301054 ok, found := containsElement(list, element)
khenaidooac637102019-01-14 15:44:34 -05001055 if !ok {
Abhay Kumara2ae5992025-11-10 14:02:24 +00001056 return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001057 }
1058 if !found {
Abhay Kumara2ae5992025-11-10 14:02:24 +00001059 return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001060 }
1061 }
1062
1063 return true
1064}
1065
Abhay Kumara2ae5992025-11-10 14:02:24 +00001066// NotSubset asserts that the list (array, slice, or map) does NOT contain all
1067// elements given in the subset (array, slice, or map).
1068// Map elements are key-value pairs unless compared with an array or slice where
1069// only the map key is evaluated.
khenaidooac637102019-01-14 15:44:34 -05001070//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001071// assert.NotSubset(t, [1, 3, 4], [1, 2])
1072// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
1073// assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"})
1074// assert.NotSubset(t, {"x": 1, "y": 2}, ["z"])
khenaidooac637102019-01-14 15:44:34 -05001075func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
1076 if h, ok := t.(tHelper); ok {
1077 h.Helper()
1078 }
1079 if subset == nil {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301080 return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001081 }
1082
khenaidooac637102019-01-14 15:44:34 -05001083 listKind := reflect.TypeOf(list).Kind()
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301084 if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
khenaidooac637102019-01-14 15:44:34 -05001085 return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
1086 }
1087
Abhay Kumara2ae5992025-11-10 14:02:24 +00001088 subsetKind := reflect.TypeOf(subset).Kind()
1089 if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
khenaidooac637102019-01-14 15:44:34 -05001090 return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
1091 }
1092
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301093 if subsetKind == reflect.Map && listKind == reflect.Map {
Abhay Kumara2ae5992025-11-10 14:02:24 +00001094 subsetMap := reflect.ValueOf(subset)
1095 actualMap := reflect.ValueOf(list)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301096
Abhay Kumara2ae5992025-11-10 14:02:24 +00001097 for _, k := range subsetMap.MapKeys() {
1098 ev := subsetMap.MapIndex(k)
1099 av := actualMap.MapIndex(k)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301100
Abhay Kumara2ae5992025-11-10 14:02:24 +00001101 if !av.IsValid() {
1102 return true
1103 }
1104 if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301105 return true
1106 }
1107 }
1108
1109 return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
1110 }
1111
Abhay Kumara2ae5992025-11-10 14:02:24 +00001112 subsetList := reflect.ValueOf(subset)
1113 if subsetKind == reflect.Map {
1114 keys := make([]interface{}, subsetList.Len())
1115 for idx, key := range subsetList.MapKeys() {
1116 keys[idx] = key.Interface()
1117 }
1118 subsetList = reflect.ValueOf(keys)
1119 }
1120 for i := 0; i < subsetList.Len(); i++ {
1121 element := subsetList.Index(i).Interface()
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301122 ok, found := containsElement(list, element)
khenaidooac637102019-01-14 15:44:34 -05001123 if !ok {
Abhay Kumara2ae5992025-11-10 14:02:24 +00001124 return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001125 }
1126 if !found {
1127 return true
1128 }
1129 }
1130
1131 return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
1132}
1133
1134// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
1135// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
1136// the number of appearances of each of them in both lists should match.
1137//
1138// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])
1139func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
1140 if h, ok := t.(tHelper); ok {
1141 h.Helper()
1142 }
1143 if isEmpty(listA) && isEmpty(listB) {
1144 return true
1145 }
1146
khenaidood948f772021-08-11 17:49:24 -04001147 if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) {
1148 return false
khenaidooac637102019-01-14 15:44:34 -05001149 }
1150
khenaidood948f772021-08-11 17:49:24 -04001151 extraA, extraB := diffLists(listA, listB)
1152
1153 if len(extraA) == 0 && len(extraB) == 0 {
1154 return true
khenaidooac637102019-01-14 15:44:34 -05001155 }
1156
khenaidood948f772021-08-11 17:49:24 -04001157 return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...)
1158}
1159
1160// isList checks that the provided value is array or slice.
1161func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) {
1162 kind := reflect.TypeOf(list).Kind()
1163 if kind != reflect.Array && kind != reflect.Slice {
1164 return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind),
1165 msgAndArgs...)
1166 }
1167 return true
1168}
1169
1170// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B.
1171// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and
1172// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored.
1173func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) {
khenaidooac637102019-01-14 15:44:34 -05001174 aValue := reflect.ValueOf(listA)
1175 bValue := reflect.ValueOf(listB)
1176
1177 aLen := aValue.Len()
1178 bLen := bValue.Len()
1179
khenaidooac637102019-01-14 15:44:34 -05001180 // Mark indexes in bValue that we already used
1181 visited := make([]bool, bLen)
1182 for i := 0; i < aLen; i++ {
1183 element := aValue.Index(i).Interface()
1184 found := false
1185 for j := 0; j < bLen; j++ {
1186 if visited[j] {
1187 continue
1188 }
1189 if ObjectsAreEqual(bValue.Index(j).Interface(), element) {
1190 visited[j] = true
1191 found = true
1192 break
1193 }
1194 }
1195 if !found {
khenaidood948f772021-08-11 17:49:24 -04001196 extraA = append(extraA, element)
khenaidooac637102019-01-14 15:44:34 -05001197 }
1198 }
1199
khenaidood948f772021-08-11 17:49:24 -04001200 for j := 0; j < bLen; j++ {
1201 if visited[j] {
1202 continue
1203 }
1204 extraB = append(extraB, bValue.Index(j).Interface())
1205 }
1206
1207 return
1208}
1209
1210func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string {
1211 var msg bytes.Buffer
1212
1213 msg.WriteString("elements differ")
1214 if len(extraA) > 0 {
1215 msg.WriteString("\n\nextra elements in list A:\n")
1216 msg.WriteString(spewConfig.Sdump(extraA))
1217 }
1218 if len(extraB) > 0 {
1219 msg.WriteString("\n\nextra elements in list B:\n")
1220 msg.WriteString(spewConfig.Sdump(extraB))
1221 }
1222 msg.WriteString("\n\nlistA:\n")
1223 msg.WriteString(spewConfig.Sdump(listA))
1224 msg.WriteString("\n\nlistB:\n")
1225 msg.WriteString(spewConfig.Sdump(listB))
1226
1227 return msg.String()
khenaidooac637102019-01-14 15:44:34 -05001228}
1229
Abhay Kumara2ae5992025-11-10 14:02:24 +00001230// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified
1231// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
1232// the number of appearances of each of them in both lists should not match.
1233// This is an inverse of ElementsMatch.
1234//
1235// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false
1236//
1237// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true
1238//
1239// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true
1240func NotElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
1241 if h, ok := t.(tHelper); ok {
1242 h.Helper()
1243 }
1244 if isEmpty(listA) && isEmpty(listB) {
1245 return Fail(t, "listA and listB contain the same elements", msgAndArgs)
1246 }
1247
1248 if !isList(t, listA, msgAndArgs...) {
1249 return Fail(t, "listA is not a list type", msgAndArgs...)
1250 }
1251 if !isList(t, listB, msgAndArgs...) {
1252 return Fail(t, "listB is not a list type", msgAndArgs...)
1253 }
1254
1255 extraA, extraB := diffLists(listA, listB)
1256 if len(extraA) == 0 && len(extraB) == 0 {
1257 return Fail(t, "listA and listB contain the same elements", msgAndArgs)
1258 }
1259
1260 return true
1261}
1262
khenaidooac637102019-01-14 15:44:34 -05001263// Condition uses a Comparison to assert a complex condition.
1264func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
1265 if h, ok := t.(tHelper); ok {
1266 h.Helper()
1267 }
1268 result := comp()
1269 if !result {
1270 Fail(t, "Condition failed!", msgAndArgs...)
1271 }
1272 return result
1273}
1274
1275// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
1276// methods, and represents a simple func that takes no arguments, and returns nothing.
1277type PanicTestFunc func()
1278
1279// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301280func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) {
1281 didPanic = true
khenaidooac637102019-01-14 15:44:34 -05001282
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301283 defer func() {
1284 message = recover()
1285 if didPanic {
1286 stack = string(debug.Stack())
1287 }
khenaidooac637102019-01-14 15:44:34 -05001288 }()
1289
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301290 // call the target function
1291 f()
1292 didPanic = false
khenaidooac637102019-01-14 15:44:34 -05001293
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301294 return
khenaidooac637102019-01-14 15:44:34 -05001295}
1296
1297// Panics asserts that the code inside the specified PanicTestFunc panics.
1298//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001299// assert.Panics(t, func(){ GoCrazy() })
khenaidooac637102019-01-14 15:44:34 -05001300func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
1301 if h, ok := t.(tHelper); ok {
1302 h.Helper()
1303 }
1304
khenaidood948f772021-08-11 17:49:24 -04001305 if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic {
khenaidooac637102019-01-14 15:44:34 -05001306 return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
1307 }
1308
1309 return true
1310}
1311
1312// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
1313// the recovered panic value equals the expected panic value.
1314//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001315// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() })
khenaidooac637102019-01-14 15:44:34 -05001316func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool {
1317 if h, ok := t.(tHelper); ok {
1318 h.Helper()
1319 }
1320
khenaidood948f772021-08-11 17:49:24 -04001321 funcDidPanic, panicValue, panickedStack := didPanic(f)
khenaidooac637102019-01-14 15:44:34 -05001322 if !funcDidPanic {
1323 return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
1324 }
1325 if panicValue != expected {
khenaidood948f772021-08-11 17:49:24 -04001326 return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...)
1327 }
1328
1329 return true
1330}
1331
1332// PanicsWithError asserts that the code inside the specified PanicTestFunc
1333// panics, and that the recovered panic value is an error that satisfies the
1334// EqualError comparison.
1335//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001336// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
khenaidood948f772021-08-11 17:49:24 -04001337func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
1338 if h, ok := t.(tHelper); ok {
1339 h.Helper()
1340 }
1341
1342 funcDidPanic, panicValue, panickedStack := didPanic(f)
1343 if !funcDidPanic {
1344 return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
1345 }
1346 panicErr, ok := panicValue.(error)
1347 if !ok || panicErr.Error() != errString {
1348 return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001349 }
1350
1351 return true
1352}
1353
1354// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
1355//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001356// assert.NotPanics(t, func(){ RemainCalm() })
khenaidooac637102019-01-14 15:44:34 -05001357func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
1358 if h, ok := t.(tHelper); ok {
1359 h.Helper()
1360 }
1361
khenaidood948f772021-08-11 17:49:24 -04001362 if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic {
1363 return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001364 }
1365
1366 return true
1367}
1368
1369// WithinDuration asserts that the two times are within duration delta of each other.
1370//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001371// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)
khenaidooac637102019-01-14 15:44:34 -05001372func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
1373 if h, ok := t.(tHelper); ok {
1374 h.Helper()
1375 }
1376
1377 dt := expected.Sub(actual)
1378 if dt < -delta || dt > delta {
1379 return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
1380 }
1381
1382 return true
1383}
1384
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301385// WithinRange asserts that a time is within a time range (inclusive).
1386//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001387// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second))
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301388func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool {
1389 if h, ok := t.(tHelper); ok {
1390 h.Helper()
1391 }
1392
1393 if end.Before(start) {
1394 return Fail(t, "Start should be before end", msgAndArgs...)
1395 }
1396
1397 if actual.Before(start) {
1398 return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...)
1399 } else if actual.After(end) {
1400 return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...)
1401 }
1402
1403 return true
1404}
1405
khenaidooac637102019-01-14 15:44:34 -05001406func toFloat(x interface{}) (float64, bool) {
1407 var xf float64
1408 xok := true
1409
1410 switch xn := x.(type) {
khenaidood948f772021-08-11 17:49:24 -04001411 case uint:
1412 xf = float64(xn)
khenaidooac637102019-01-14 15:44:34 -05001413 case uint8:
1414 xf = float64(xn)
1415 case uint16:
1416 xf = float64(xn)
1417 case uint32:
1418 xf = float64(xn)
1419 case uint64:
1420 xf = float64(xn)
1421 case int:
1422 xf = float64(xn)
1423 case int8:
1424 xf = float64(xn)
1425 case int16:
1426 xf = float64(xn)
1427 case int32:
1428 xf = float64(xn)
1429 case int64:
1430 xf = float64(xn)
1431 case float32:
1432 xf = float64(xn)
1433 case float64:
khenaidood948f772021-08-11 17:49:24 -04001434 xf = xn
khenaidooac637102019-01-14 15:44:34 -05001435 case time.Duration:
1436 xf = float64(xn)
1437 default:
1438 xok = false
1439 }
1440
1441 return xf, xok
1442}
1443
1444// InDelta asserts that the two numerals are within delta of each other.
1445//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001446// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
khenaidooac637102019-01-14 15:44:34 -05001447func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
1448 if h, ok := t.(tHelper); ok {
1449 h.Helper()
1450 }
1451
1452 af, aok := toFloat(expected)
1453 bf, bok := toFloat(actual)
1454
1455 if !aok || !bok {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301456 return Fail(t, "Parameters must be numerical", msgAndArgs...)
1457 }
1458
1459 if math.IsNaN(af) && math.IsNaN(bf) {
1460 return true
khenaidooac637102019-01-14 15:44:34 -05001461 }
1462
1463 if math.IsNaN(af) {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301464 return Fail(t, "Expected must not be NaN", msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001465 }
1466
1467 if math.IsNaN(bf) {
1468 return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...)
1469 }
1470
1471 dt := af - bf
1472 if dt < -delta || dt > delta {
1473 return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
1474 }
1475
1476 return true
1477}
1478
1479// InDeltaSlice is the same as InDelta, except it compares two slices.
1480func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
1481 if h, ok := t.(tHelper); ok {
1482 h.Helper()
1483 }
1484 if expected == nil || actual == nil ||
1485 reflect.TypeOf(actual).Kind() != reflect.Slice ||
1486 reflect.TypeOf(expected).Kind() != reflect.Slice {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301487 return Fail(t, "Parameters must be slice", msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001488 }
1489
1490 actualSlice := reflect.ValueOf(actual)
1491 expectedSlice := reflect.ValueOf(expected)
1492
1493 for i := 0; i < actualSlice.Len(); i++ {
1494 result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...)
1495 if !result {
1496 return result
1497 }
1498 }
1499
1500 return true
1501}
1502
1503// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
1504func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
1505 if h, ok := t.(tHelper); ok {
1506 h.Helper()
1507 }
1508 if expected == nil || actual == nil ||
1509 reflect.TypeOf(actual).Kind() != reflect.Map ||
1510 reflect.TypeOf(expected).Kind() != reflect.Map {
1511 return Fail(t, "Arguments must be maps", msgAndArgs...)
1512 }
1513
1514 expectedMap := reflect.ValueOf(expected)
1515 actualMap := reflect.ValueOf(actual)
1516
1517 if expectedMap.Len() != actualMap.Len() {
1518 return Fail(t, "Arguments must have the same number of keys", msgAndArgs...)
1519 }
1520
1521 for _, k := range expectedMap.MapKeys() {
1522 ev := expectedMap.MapIndex(k)
1523 av := actualMap.MapIndex(k)
1524
1525 if !ev.IsValid() {
1526 return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...)
1527 }
1528
1529 if !av.IsValid() {
1530 return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...)
1531 }
1532
1533 if !InDelta(
1534 t,
1535 ev.Interface(),
1536 av.Interface(),
1537 delta,
1538 msgAndArgs...,
1539 ) {
1540 return false
1541 }
1542 }
1543
1544 return true
1545}
1546
1547func calcRelativeError(expected, actual interface{}) (float64, error) {
1548 af, aok := toFloat(expected)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301549 bf, bok := toFloat(actual)
1550 if !aok || !bok {
1551 return 0, fmt.Errorf("Parameters must be numerical")
1552 }
1553 if math.IsNaN(af) && math.IsNaN(bf) {
1554 return 0, nil
khenaidooac637102019-01-14 15:44:34 -05001555 }
khenaidood948f772021-08-11 17:49:24 -04001556 if math.IsNaN(af) {
1557 return 0, errors.New("expected value must not be NaN")
1558 }
khenaidooac637102019-01-14 15:44:34 -05001559 if af == 0 {
1560 return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
1561 }
khenaidood948f772021-08-11 17:49:24 -04001562 if math.IsNaN(bf) {
1563 return 0, errors.New("actual value must not be NaN")
1564 }
khenaidooac637102019-01-14 15:44:34 -05001565
1566 return math.Abs(af-bf) / math.Abs(af), nil
1567}
1568
1569// InEpsilon asserts that expected and actual have a relative error less than epsilon
1570func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
1571 if h, ok := t.(tHelper); ok {
1572 h.Helper()
1573 }
khenaidood948f772021-08-11 17:49:24 -04001574 if math.IsNaN(epsilon) {
Abhay Kumara2ae5992025-11-10 14:02:24 +00001575 return Fail(t, "epsilon must not be NaN", msgAndArgs...)
khenaidood948f772021-08-11 17:49:24 -04001576 }
khenaidooac637102019-01-14 15:44:34 -05001577 actualEpsilon, err := calcRelativeError(expected, actual)
1578 if err != nil {
1579 return Fail(t, err.Error(), msgAndArgs...)
1580 }
Abhay Kumara2ae5992025-11-10 14:02:24 +00001581 if math.IsNaN(actualEpsilon) {
1582 return Fail(t, "relative error is NaN", msgAndArgs...)
1583 }
khenaidooac637102019-01-14 15:44:34 -05001584 if actualEpsilon > epsilon {
1585 return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
1586 " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...)
1587 }
1588
1589 return true
1590}
1591
1592// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
1593func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
1594 if h, ok := t.(tHelper); ok {
1595 h.Helper()
1596 }
Abhay Kumara2ae5992025-11-10 14:02:24 +00001597
1598 if expected == nil || actual == nil {
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301599 return Fail(t, "Parameters must be slice", msgAndArgs...)
khenaidooac637102019-01-14 15:44:34 -05001600 }
1601
khenaidooac637102019-01-14 15:44:34 -05001602 expectedSlice := reflect.ValueOf(expected)
Abhay Kumara2ae5992025-11-10 14:02:24 +00001603 actualSlice := reflect.ValueOf(actual)
khenaidooac637102019-01-14 15:44:34 -05001604
Abhay Kumara2ae5992025-11-10 14:02:24 +00001605 if expectedSlice.Type().Kind() != reflect.Slice {
1606 return Fail(t, "Expected value must be slice", msgAndArgs...)
1607 }
1608
1609 expectedLen := expectedSlice.Len()
1610 if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) {
1611 return false
1612 }
1613
1614 for i := 0; i < expectedLen; i++ {
1615 if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) {
1616 return false
khenaidooac637102019-01-14 15:44:34 -05001617 }
1618 }
1619
1620 return true
1621}
1622
1623/*
1624 Errors
1625*/
1626
1627// NoError asserts that a function returned no error (i.e. `nil`).
1628//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001629// actualObj, err := SomeFunction()
1630// if assert.NoError(t, err) {
1631// assert.Equal(t, expectedObj, actualObj)
1632// }
khenaidooac637102019-01-14 15:44:34 -05001633func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -05001634 if err != nil {
khenaidood948f772021-08-11 17:49:24 -04001635 if h, ok := t.(tHelper); ok {
1636 h.Helper()
1637 }
khenaidooac637102019-01-14 15:44:34 -05001638 return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
1639 }
1640
1641 return true
1642}
1643
1644// Error asserts that a function returned an error (i.e. not `nil`).
1645//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001646// actualObj, err := SomeFunction()
1647// assert.Error(t, err)
khenaidooac637102019-01-14 15:44:34 -05001648func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -05001649 if err == nil {
khenaidood948f772021-08-11 17:49:24 -04001650 if h, ok := t.(tHelper); ok {
1651 h.Helper()
1652 }
khenaidooac637102019-01-14 15:44:34 -05001653 return Fail(t, "An error is expected but got nil.", msgAndArgs...)
1654 }
1655
1656 return true
1657}
1658
1659// EqualError asserts that a function returned an error (i.e. not `nil`)
1660// and that it is equal to the provided error.
1661//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001662// actualObj, err := SomeFunction()
1663// assert.EqualError(t, err, expectedErrorString)
khenaidooac637102019-01-14 15:44:34 -05001664func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
1665 if h, ok := t.(tHelper); ok {
1666 h.Helper()
1667 }
1668 if !Error(t, theError, msgAndArgs...) {
1669 return false
1670 }
1671 expected := errString
1672 actual := theError.Error()
1673 // don't need to use deep equals here, we know they are both strings
1674 if expected != actual {
1675 return Fail(t, fmt.Sprintf("Error message not equal:\n"+
1676 "expected: %q\n"+
1677 "actual : %q", expected, actual), msgAndArgs...)
1678 }
1679 return true
1680}
1681
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301682// ErrorContains asserts that a function returned an error (i.e. not `nil`)
1683// and that the error contains the specified substring.
1684//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001685// actualObj, err := SomeFunction()
1686// assert.ErrorContains(t, err, expectedErrorSubString)
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301687func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool {
1688 if h, ok := t.(tHelper); ok {
1689 h.Helper()
1690 }
1691 if !Error(t, theError, msgAndArgs...) {
1692 return false
1693 }
1694
1695 actual := theError.Error()
1696 if !strings.Contains(actual, contains) {
1697 return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...)
1698 }
1699
1700 return true
1701}
1702
khenaidooac637102019-01-14 15:44:34 -05001703// matchRegexp return true if a specified regexp matches a string.
1704func matchRegexp(rx interface{}, str interface{}) bool {
khenaidooac637102019-01-14 15:44:34 -05001705 var r *regexp.Regexp
1706 if rr, ok := rx.(*regexp.Regexp); ok {
1707 r = rr
1708 } else {
1709 r = regexp.MustCompile(fmt.Sprint(rx))
1710 }
1711
Abhay Kumara2ae5992025-11-10 14:02:24 +00001712 switch v := str.(type) {
1713 case []byte:
1714 return r.Match(v)
1715 case string:
1716 return r.MatchString(v)
1717 default:
1718 return r.MatchString(fmt.Sprint(v))
1719 }
khenaidooac637102019-01-14 15:44:34 -05001720}
1721
1722// Regexp asserts that a specified regexp matches a string.
1723//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001724// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
1725// assert.Regexp(t, "start...$", "it's not starting")
khenaidooac637102019-01-14 15:44:34 -05001726func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
1727 if h, ok := t.(tHelper); ok {
1728 h.Helper()
1729 }
1730
1731 match := matchRegexp(rx, str)
1732
1733 if !match {
1734 Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...)
1735 }
1736
1737 return match
1738}
1739
1740// NotRegexp asserts that a specified regexp does not match a string.
1741//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001742// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
1743// assert.NotRegexp(t, "^start", "it's not starting")
khenaidooac637102019-01-14 15:44:34 -05001744func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
1745 if h, ok := t.(tHelper); ok {
1746 h.Helper()
1747 }
1748 match := matchRegexp(rx, str)
1749
1750 if match {
1751 Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...)
1752 }
1753
1754 return !match
khenaidooac637102019-01-14 15:44:34 -05001755}
1756
1757// Zero asserts that i is the zero value for its type.
1758func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
1759 if h, ok := t.(tHelper); ok {
1760 h.Helper()
1761 }
1762 if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
1763 return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
1764 }
1765 return true
1766}
1767
1768// NotZero asserts that i is not the zero value for its type.
1769func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
1770 if h, ok := t.(tHelper); ok {
1771 h.Helper()
1772 }
1773 if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
1774 return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
1775 }
1776 return true
1777}
1778
khenaidood948f772021-08-11 17:49:24 -04001779// FileExists checks whether a file exists in the given path. It also fails if
1780// the path points to a directory or there is an error when trying to check the file.
khenaidooac637102019-01-14 15:44:34 -05001781func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
1782 if h, ok := t.(tHelper); ok {
1783 h.Helper()
1784 }
1785 info, err := os.Lstat(path)
1786 if err != nil {
1787 if os.IsNotExist(err) {
1788 return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
1789 }
1790 return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
1791 }
1792 if info.IsDir() {
1793 return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...)
1794 }
1795 return true
1796}
1797
khenaidood948f772021-08-11 17:49:24 -04001798// NoFileExists checks whether a file does not exist in a given path. It fails
1799// if the path points to an existing _file_ only.
1800func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
1801 if h, ok := t.(tHelper); ok {
1802 h.Helper()
1803 }
1804 info, err := os.Lstat(path)
1805 if err != nil {
1806 return true
1807 }
1808 if info.IsDir() {
1809 return true
1810 }
1811 return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...)
1812}
1813
1814// DirExists checks whether a directory exists in the given path. It also fails
1815// if the path is a file rather a directory or there is an error checking whether it exists.
khenaidooac637102019-01-14 15:44:34 -05001816func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
1817 if h, ok := t.(tHelper); ok {
1818 h.Helper()
1819 }
1820 info, err := os.Lstat(path)
1821 if err != nil {
1822 if os.IsNotExist(err) {
1823 return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...)
1824 }
1825 return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...)
1826 }
1827 if !info.IsDir() {
1828 return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...)
1829 }
1830 return true
1831}
1832
khenaidood948f772021-08-11 17:49:24 -04001833// NoDirExists checks whether a directory does not exist in the given path.
1834// It fails if the path points to an existing _directory_ only.
1835func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
1836 if h, ok := t.(tHelper); ok {
1837 h.Helper()
1838 }
1839 info, err := os.Lstat(path)
1840 if err != nil {
1841 if os.IsNotExist(err) {
1842 return true
1843 }
1844 return true
1845 }
1846 if !info.IsDir() {
1847 return true
1848 }
1849 return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...)
1850}
1851
khenaidooac637102019-01-14 15:44:34 -05001852// JSONEq asserts that two JSON strings are equivalent.
1853//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001854// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
khenaidooac637102019-01-14 15:44:34 -05001855func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
1856 if h, ok := t.(tHelper); ok {
1857 h.Helper()
1858 }
1859 var expectedJSONAsInterface, actualJSONAsInterface interface{}
1860
1861 if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil {
1862 return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...)
1863 }
1864
Abhay Kumara2ae5992025-11-10 14:02:24 +00001865 // Shortcut if same bytes
1866 if actual == expected {
1867 return true
1868 }
1869
khenaidooac637102019-01-14 15:44:34 -05001870 if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
1871 return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...)
1872 }
1873
1874 return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
1875}
1876
Scott Baker8461e152019-10-01 14:44:30 -07001877// YAMLEq asserts that two YAML strings are equivalent.
1878func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
1879 if h, ok := t.(tHelper); ok {
1880 h.Helper()
1881 }
1882 var expectedYAMLAsInterface, actualYAMLAsInterface interface{}
1883
1884 if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil {
1885 return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...)
1886 }
1887
Abhay Kumara2ae5992025-11-10 14:02:24 +00001888 // Shortcut if same bytes
1889 if actual == expected {
1890 return true
1891 }
1892
Scott Baker8461e152019-10-01 14:44:30 -07001893 if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil {
1894 return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...)
1895 }
1896
1897 return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...)
1898}
1899
khenaidooac637102019-01-14 15:44:34 -05001900func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
1901 t := reflect.TypeOf(v)
1902 k := t.Kind()
1903
1904 if k == reflect.Ptr {
1905 t = t.Elem()
1906 k = t.Kind()
1907 }
1908 return t, k
1909}
1910
1911// diff returns a diff of both values as long as both are of the same type and
1912// are a struct, map, slice, array or string. Otherwise it returns an empty string.
1913func diff(expected interface{}, actual interface{}) string {
1914 if expected == nil || actual == nil {
1915 return ""
1916 }
1917
1918 et, ek := typeAndKind(expected)
1919 at, _ := typeAndKind(actual)
1920
1921 if et != at {
1922 return ""
1923 }
1924
1925 if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String {
1926 return ""
1927 }
1928
1929 var e, a string
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301930
1931 switch et {
1932 case reflect.TypeOf(""):
Scott Baker8461e152019-10-01 14:44:30 -07001933 e = reflect.ValueOf(expected).String()
1934 a = reflect.ValueOf(actual).String()
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301935 case reflect.TypeOf(time.Time{}):
1936 e = spewConfigStringerEnabled.Sdump(expected)
1937 a = spewConfigStringerEnabled.Sdump(actual)
1938 default:
1939 e = spewConfig.Sdump(expected)
1940 a = spewConfig.Sdump(actual)
khenaidooac637102019-01-14 15:44:34 -05001941 }
1942
1943 diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
1944 A: difflib.SplitLines(e),
1945 B: difflib.SplitLines(a),
1946 FromFile: "Expected",
1947 FromDate: "",
1948 ToFile: "Actual",
1949 ToDate: "",
1950 Context: 1,
1951 })
1952
1953 return "\n\nDiff:\n" + diff
1954}
1955
khenaidooac637102019-01-14 15:44:34 -05001956func isFunction(arg interface{}) bool {
1957 if arg == nil {
1958 return false
1959 }
1960 return reflect.TypeOf(arg).Kind() == reflect.Func
1961}
1962
1963var spewConfig = spew.ConfigState{
1964 Indent: " ",
1965 DisablePointerAddresses: true,
1966 DisableCapacities: true,
1967 SortKeys: true,
khenaidood948f772021-08-11 17:49:24 -04001968 DisableMethods: true,
1969 MaxDepth: 10,
khenaidooac637102019-01-14 15:44:34 -05001970}
1971
Akash Reddy Kankanalacf045372025-06-10 14:11:24 +05301972var spewConfigStringerEnabled = spew.ConfigState{
1973 Indent: " ",
1974 DisablePointerAddresses: true,
1975 DisableCapacities: true,
1976 SortKeys: true,
1977 MaxDepth: 10,
1978}
1979
Abhay Kumara2ae5992025-11-10 14:02:24 +00001980type tHelper = interface {
khenaidooac637102019-01-14 15:44:34 -05001981 Helper()
1982}
Scott Baker8461e152019-10-01 14:44:30 -07001983
1984// Eventually asserts that given condition will be met in waitFor time,
1985// periodically checking target function each tick.
1986//
Abhay Kumara2ae5992025-11-10 14:02:24 +00001987// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
Scott Baker8461e152019-10-01 14:44:30 -07001988func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
1989 if h, ok := t.(tHelper); ok {
1990 h.Helper()
1991 }
1992
khenaidood948f772021-08-11 17:49:24 -04001993 ch := make(chan bool, 1)
Abhay Kumara2ae5992025-11-10 14:02:24 +00001994 checkCond := func() { ch <- condition() }
khenaidood948f772021-08-11 17:49:24 -04001995
Scott Baker8461e152019-10-01 14:44:30 -07001996 timer := time.NewTimer(waitFor)
Scott Baker8461e152019-10-01 14:44:30 -07001997 defer timer.Stop()
khenaidood948f772021-08-11 17:49:24 -04001998
1999 ticker := time.NewTicker(tick)
Scott Baker8461e152019-10-01 14:44:30 -07002000 defer ticker.Stop()
khenaidood948f772021-08-11 17:49:24 -04002001
Abhay Kumara2ae5992025-11-10 14:02:24 +00002002 var tickC <-chan time.Time
2003
2004 // Check the condition once first on the initial call.
2005 go checkCond()
2006
2007 for {
Scott Baker8461e152019-10-01 14:44:30 -07002008 select {
2009 case <-timer.C:
2010 return Fail(t, "Condition never satisfied", msgAndArgs...)
Abhay Kumara2ae5992025-11-10 14:02:24 +00002011 case <-tickC:
2012 tickC = nil
2013 go checkCond()
khenaidood948f772021-08-11 17:49:24 -04002014 case v := <-ch:
2015 if v {
Scott Baker8461e152019-10-01 14:44:30 -07002016 return true
2017 }
Abhay Kumara2ae5992025-11-10 14:02:24 +00002018 tickC = ticker.C
2019 }
2020 }
2021}
2022
2023// CollectT implements the TestingT interface and collects all errors.
2024type CollectT struct {
2025 // A slice of errors. Non-nil slice denotes a failure.
2026 // If it's non-nil but len(c.errors) == 0, this is also a failure
2027 // obtained by direct c.FailNow() call.
2028 errors []error
2029}
2030
2031// Helper is like [testing.T.Helper] but does nothing.
2032func (CollectT) Helper() {}
2033
2034// Errorf collects the error.
2035func (c *CollectT) Errorf(format string, args ...interface{}) {
2036 c.errors = append(c.errors, fmt.Errorf(format, args...))
2037}
2038
2039// FailNow stops execution by calling runtime.Goexit.
2040func (c *CollectT) FailNow() {
2041 c.fail()
2042 runtime.Goexit()
2043}
2044
2045// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
2046func (*CollectT) Reset() {
2047 panic("Reset() is deprecated")
2048}
2049
2050// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
2051func (*CollectT) Copy(TestingT) {
2052 panic("Copy() is deprecated")
2053}
2054
2055func (c *CollectT) fail() {
2056 if !c.failed() {
2057 c.errors = []error{} // Make it non-nil to mark a failure.
2058 }
2059}
2060
2061func (c *CollectT) failed() bool {
2062 return c.errors != nil
2063}
2064
2065// EventuallyWithT asserts that given condition will be met in waitFor time,
2066// periodically checking target function each tick. In contrast to Eventually,
2067// it supplies a CollectT to the condition function, so that the condition
2068// function can use the CollectT to call other assertions.
2069// The condition is considered "met" if no errors are raised in a tick.
2070// The supplied CollectT collects all errors from one tick (if there are any).
2071// If the condition is not met before waitFor, the collected errors of
2072// the last tick are copied to t.
2073//
2074// externalValue := false
2075// go func() {
2076// time.Sleep(8*time.Second)
2077// externalValue = true
2078// }()
2079// assert.EventuallyWithT(t, func(c *assert.CollectT) {
2080// // add assertions as needed; any assertion failure will fail the current tick
2081// assert.True(c, externalValue, "expected 'externalValue' to be true")
2082// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false")
2083func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
2084 if h, ok := t.(tHelper); ok {
2085 h.Helper()
2086 }
2087
2088 var lastFinishedTickErrs []error
2089 ch := make(chan *CollectT, 1)
2090
2091 checkCond := func() {
2092 collect := new(CollectT)
2093 defer func() {
2094 ch <- collect
2095 }()
2096 condition(collect)
2097 }
2098
2099 timer := time.NewTimer(waitFor)
2100 defer timer.Stop()
2101
2102 ticker := time.NewTicker(tick)
2103 defer ticker.Stop()
2104
2105 var tickC <-chan time.Time
2106
2107 // Check the condition once first on the initial call.
2108 go checkCond()
2109
2110 for {
2111 select {
2112 case <-timer.C:
2113 for _, err := range lastFinishedTickErrs {
2114 t.Errorf("%v", err)
2115 }
2116 return Fail(t, "Condition never satisfied", msgAndArgs...)
2117 case <-tickC:
2118 tickC = nil
2119 go checkCond()
2120 case collect := <-ch:
2121 if !collect.failed() {
2122 return true
2123 }
2124 // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
2125 lastFinishedTickErrs = collect.errors
2126 tickC = ticker.C
Scott Baker8461e152019-10-01 14:44:30 -07002127 }
2128 }
2129}
khenaidood948f772021-08-11 17:49:24 -04002130
2131// Never asserts that the given condition doesn't satisfy in waitFor time,
2132// periodically checking the target function each tick.
2133//
Abhay Kumara2ae5992025-11-10 14:02:24 +00002134// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
khenaidood948f772021-08-11 17:49:24 -04002135func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
2136 if h, ok := t.(tHelper); ok {
2137 h.Helper()
2138 }
2139
2140 ch := make(chan bool, 1)
Abhay Kumara2ae5992025-11-10 14:02:24 +00002141 checkCond := func() { ch <- condition() }
khenaidood948f772021-08-11 17:49:24 -04002142
2143 timer := time.NewTimer(waitFor)
2144 defer timer.Stop()
2145
2146 ticker := time.NewTicker(tick)
2147 defer ticker.Stop()
2148
Abhay Kumara2ae5992025-11-10 14:02:24 +00002149 var tickC <-chan time.Time
2150
2151 // Check the condition once first on the initial call.
2152 go checkCond()
2153
2154 for {
khenaidood948f772021-08-11 17:49:24 -04002155 select {
2156 case <-timer.C:
2157 return true
Abhay Kumara2ae5992025-11-10 14:02:24 +00002158 case <-tickC:
2159 tickC = nil
2160 go checkCond()
khenaidood948f772021-08-11 17:49:24 -04002161 case v := <-ch:
2162 if v {
2163 return Fail(t, "Condition satisfied", msgAndArgs...)
2164 }
Abhay Kumara2ae5992025-11-10 14:02:24 +00002165 tickC = ticker.C
khenaidood948f772021-08-11 17:49:24 -04002166 }
2167 }
2168}
2169
2170// ErrorIs asserts that at least one of the errors in err's chain matches target.
2171// This is a wrapper for errors.Is.
2172func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
2173 if h, ok := t.(tHelper); ok {
2174 h.Helper()
2175 }
2176 if errors.Is(err, target) {
2177 return true
2178 }
2179
2180 var expectedText string
2181 if target != nil {
2182 expectedText = target.Error()
Abhay Kumara2ae5992025-11-10 14:02:24 +00002183 if err == nil {
2184 return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...)
2185 }
khenaidood948f772021-08-11 17:49:24 -04002186 }
2187
Abhay Kumara2ae5992025-11-10 14:02:24 +00002188 chain := buildErrorChainString(err, false)
khenaidood948f772021-08-11 17:49:24 -04002189
2190 return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
2191 "expected: %q\n"+
2192 "in chain: %s", expectedText, chain,
2193 ), msgAndArgs...)
2194}
2195
Abhay Kumara2ae5992025-11-10 14:02:24 +00002196// NotErrorIs asserts that none of the errors in err's chain matches target.
khenaidood948f772021-08-11 17:49:24 -04002197// This is a wrapper for errors.Is.
2198func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
2199 if h, ok := t.(tHelper); ok {
2200 h.Helper()
2201 }
2202 if !errors.Is(err, target) {
2203 return true
2204 }
2205
2206 var expectedText string
2207 if target != nil {
2208 expectedText = target.Error()
2209 }
2210
Abhay Kumara2ae5992025-11-10 14:02:24 +00002211 chain := buildErrorChainString(err, false)
khenaidood948f772021-08-11 17:49:24 -04002212
2213 return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
2214 "found: %q\n"+
2215 "in chain: %s", expectedText, chain,
2216 ), msgAndArgs...)
2217}
2218
2219// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value.
2220// This is a wrapper for errors.As.
2221func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
2222 if h, ok := t.(tHelper); ok {
2223 h.Helper()
2224 }
2225 if errors.As(err, target) {
2226 return true
2227 }
2228
Abhay Kumara2ae5992025-11-10 14:02:24 +00002229 expectedType := reflect.TypeOf(target).Elem().String()
2230 if err == nil {
2231 return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+
2232 "expected: %s", expectedType), msgAndArgs...)
2233 }
2234
2235 chain := buildErrorChainString(err, true)
khenaidood948f772021-08-11 17:49:24 -04002236
2237 return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
Abhay Kumara2ae5992025-11-10 14:02:24 +00002238 "expected: %s\n"+
2239 "in chain: %s", expectedType, chain,
khenaidood948f772021-08-11 17:49:24 -04002240 ), msgAndArgs...)
2241}
2242
Abhay Kumara2ae5992025-11-10 14:02:24 +00002243// NotErrorAs asserts that none of the errors in err's chain matches target,
2244// but if so, sets target to that error value.
2245func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool {
2246 if h, ok := t.(tHelper); ok {
2247 h.Helper()
2248 }
2249 if !errors.As(err, target) {
2250 return true
2251 }
2252
2253 chain := buildErrorChainString(err, true)
2254
2255 return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
2256 "found: %s\n"+
2257 "in chain: %s", reflect.TypeOf(target).Elem().String(), chain,
2258 ), msgAndArgs...)
2259}
2260
2261func unwrapAll(err error) (errs []error) {
2262 errs = append(errs, err)
2263 switch x := err.(type) {
2264 case interface{ Unwrap() error }:
2265 err = x.Unwrap()
2266 if err == nil {
2267 return
2268 }
2269 errs = append(errs, unwrapAll(err)...)
2270 case interface{ Unwrap() []error }:
2271 for _, err := range x.Unwrap() {
2272 errs = append(errs, unwrapAll(err)...)
2273 }
2274 }
2275 return
2276}
2277
2278func buildErrorChainString(err error, withType bool) string {
khenaidood948f772021-08-11 17:49:24 -04002279 if err == nil {
2280 return ""
2281 }
2282
Abhay Kumara2ae5992025-11-10 14:02:24 +00002283 var chain string
2284 errs := unwrapAll(err)
2285 for i := range errs {
2286 if i != 0 {
2287 chain += "\n\t"
2288 }
2289 chain += fmt.Sprintf("%q", errs[i].Error())
2290 if withType {
2291 chain += fmt.Sprintf(" (%T)", errs[i])
2292 }
khenaidood948f772021-08-11 17:49:24 -04002293 }
2294 return chain
2295}