Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assert/assertion_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// assert.Emptyf(t, obj, "error message %s", "formatted")
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand Down
4 changes: 4 additions & 0 deletions assert/assertion_forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// a.Empty(obj)
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand All @@ -122,6 +124,8 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// a.Emptyf(obj, "error message %s", "formatted")
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand Down
7 changes: 5 additions & 2 deletions assert/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,8 +768,9 @@ func isEmptyValue(objValue reflect.Value) bool {
// Note: array types are empty when they match their zero-initialized state.
case reflect.Chan, reflect.Map, reflect.Slice:
return objValue.Len() == 0
// non-nil pointers are empty if the value they point to is empty
case reflect.Ptr:
// non-nil pointers are empty if the value they point to is empty,
// and likewise for non-nil interfaces and the value they hold
case reflect.Ptr, reflect.Interface:
return isEmptyValue(objValue.Elem())
}
return false
Expand All @@ -785,6 +786,8 @@ func isEmptyValue(objValue reflect.Value) bool {
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// assert.Empty(t, obj)
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand Down
42 changes: 42 additions & 0 deletions assert/assertions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,19 @@ func Test_isEmpty(t *testing.T) {
chWithValue := make(chan struct{}, 1)
chWithValue <- struct{}{}

// Values held in an interface, so that &x below is a pointer to an interface.
var (
emptyStringAny interface{} = ""
emptyIntAny interface{} = 0
emptySliceAny interface{} = []int{}
emptyPtrAny interface{} = new(int)
emptyErr = errors.New("") // error interface holding a non-nil, empty-message error
nonEmptyStringAny interface{} = "something"
nonEmptyIntAny interface{} = 42
nonEmptySliceAny interface{} = []int{42}
nonEmptyErr = errors.New("something")
)

True(t, isEmpty(""))
True(t, isEmpty(nil))
True(t, isEmpty(error(nil)))
Expand Down Expand Up @@ -1826,6 +1839,12 @@ func Test_isEmpty(t *testing.T) {
True(t, isEmpty([]error(nil)))
True(t, isEmpty(&[1]int{0}))
True(t, isEmpty(&[2]int{0, 0}))
True(t, isEmpty(new(error))) // ptr to nil interface
True(t, isEmpty(&emptyStringAny)) // ptr to interface holding ""
True(t, isEmpty(&emptyIntAny)) // ptr to interface holding 0
True(t, isEmpty(&emptySliceAny)) // ptr to interface holding []int{}
True(t, isEmpty(&emptyPtrAny)) // ptr to interface holding a ptr to a zero value
True(t, isEmpty(&emptyErr)) // ptr to error holding errors.New("") - BEWARE
False(t, isEmpty("something"))
False(t, isEmpty(errors.New("something")))
False(t, isEmpty([]string{"something"}))
Expand All @@ -1852,6 +1871,10 @@ func Test_isEmpty(t *testing.T) {
False(t, isEmpty([]bool{false})) // elements values are ignored for slices
False(t, isEmpty([]bool{true})) // elements values are ignored for slices
False(t, isEmpty([]error{errors.New("xxx")}))
False(t, isEmpty(&nonEmptyStringAny)) // ptr to interface holding "something"
False(t, isEmpty(&nonEmptyIntAny)) // ptr to interface holding 42
False(t, isEmpty(&nonEmptySliceAny)) // ptr to interface holding []int{42}
False(t, isEmpty(&nonEmptyErr)) // ptr to error holding errors.New("something")
False(t, isEmpty([]error{nil})) // BEWARE
False(t, isEmpty([]error{errors.New("")})) // BEWARE
False(t, isEmpty(map[string]string{"Hello": "World"}))
Expand Down Expand Up @@ -1915,6 +1938,13 @@ func TestEmpty(t *testing.T) {
x int
}

// Pointers to an interface: Empty(&i) must agree with Empty(i).
var emptyIface interface{} = ""
var nonEmptyIface interface{} = "something"
var nilIface interface{}
var emptyStructIface interface{} = &TStruct{}
var nonEmptyStructIface interface{} = &TStruct{x: 1}

True(t, Empty(mockT, ""), "Empty string is empty")
True(t, Empty(mockT, nil), "Nil is empty")
True(t, Empty(mockT, []string{}), "Empty string array is empty")
Expand All @@ -1929,6 +1959,9 @@ func TestEmpty(t *testing.T) {
True(t, Empty(mockT, TString("")), "empty aliased string is empty")
True(t, Empty(mockT, sP), "ptr to nil value is empty")
True(t, Empty(mockT, [1]int{}), "array is state")
True(t, Empty(mockT, &nilIface), "ptr to nil interface is empty")
True(t, Empty(mockT, &emptyIface), "ptr to interface holding an empty value is empty")
True(t, Empty(mockT, &emptyStructIface), "ptr to interface holding a ptr to an empty struct is empty")

False(t, Empty(mockT, "something"), "Non Empty string is not empty")
False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty")
Expand All @@ -1940,6 +1973,8 @@ func TestEmpty(t *testing.T) {
False(t, Empty(mockT, TString("abc")), "non-empty aliased string is empty")
False(t, Empty(mockT, xP), "ptr to non-nil value is not empty")
False(t, Empty(mockT, [1]int{42}), "array is not state")
False(t, Empty(mockT, &nonEmptyIface), "ptr to interface holding a non-empty value is not empty")
False(t, Empty(mockT, &nonEmptyStructIface), "ptr to interface holding a ptr to a non-empty struct is not empty")

// error messages validation
tests := []struct {
Expand Down Expand Up @@ -2085,13 +2120,19 @@ func TestNotEmpty(t *testing.T) {
chWithValue := make(chan struct{}, 1)
chWithValue <- struct{}{}

var emptyIface interface{} = ""
var nonEmptyIface interface{} = "something"
var nilIface interface{}

False(t, NotEmpty(mockT, ""), "Empty string is empty")
False(t, NotEmpty(mockT, nil), "Nil is empty")
False(t, NotEmpty(mockT, []string{}), "Empty string array is empty")
False(t, NotEmpty(mockT, 0), "Zero int value is empty")
False(t, NotEmpty(mockT, false), "False value is empty")
False(t, NotEmpty(mockT, make(chan struct{})), "Channel without values is empty")
False(t, NotEmpty(mockT, [1]int{}), "array is state")
False(t, NotEmpty(mockT, &nilIface), "ptr to nil interface is empty")
False(t, NotEmpty(mockT, &emptyIface), "ptr to interface holding an empty value is empty")

True(t, NotEmpty(mockT, "something"), "Non Empty string is not empty")
True(t, NotEmpty(mockT, errors.New("something")), "Non nil object is not empty")
Expand All @@ -2100,6 +2141,7 @@ func TestNotEmpty(t *testing.T) {
True(t, NotEmpty(mockT, true), "True value is not empty")
True(t, NotEmpty(mockT, chWithValue), "Channel with values is not empty")
True(t, NotEmpty(mockT, [1]int{42}), "array is not state")
True(t, NotEmpty(mockT, &nonEmptyIface), "ptr to interface holding a non-empty value is not empty")

// error messages validation
tests := []struct {
Expand Down
4 changes: 4 additions & 0 deletions require/require.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// require.Empty(t, obj)
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand All @@ -150,6 +152,8 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// require.Emptyf(t, obj, "error message %s", "formatted")
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand Down
4 changes: 4 additions & 0 deletions require/require_forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// a.Empty(obj)
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand All @@ -123,6 +125,8 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
//
// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// Interface values are "empty" if the interface is nil or if the value it holds is "empty".
//
// a.Emptyf(obj, "error message %s", "formatted")
//
// [Zero values]: https://go.dev/ref/spec#The_zero_value
Expand Down