generalize Empty assertion

Make `Empty` work against any struct and custom types, by replacing
explicit zero value comparisons with a `DeepEqual` comparison with
the type's `reflect.ZeroValue`.
pull/523/merge
Vincent Cote-Roy 2017-12-10 12:56:05 -05:00 committed by Ernesto Jiménez
parent 2aa2c176b9
commit 88a414d072
2 changed files with 28 additions and 45 deletions

View File

@ -414,65 +414,33 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
} }
var numericZeros = []interface{}{
int(0),
int8(0),
int16(0),
int32(0),
int64(0),
uint(0),
uint8(0),
uint16(0),
uint32(0),
uint64(0),
float32(0),
float64(0),
}
// isEmpty gets whether the specified object is considered empty or not. // isEmpty gets whether the specified object is considered empty or not.
func isEmpty(object interface{}) bool { func isEmpty(object interface{}) bool {
// get nil case out of the way
if object == nil { if object == nil {
return true return true
} else if object == "" {
return true
} else if object == false {
return true
}
for _, v := range numericZeros {
if object == v {
return true
}
} }
objValue := reflect.ValueOf(object) objValue := reflect.ValueOf(object)
switch objValue.Kind() { switch objValue.Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: // collection types are empty when they have no element
{ case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
return (objValue.Len() == 0) return objValue.Len() == 0
} // pointers are empty if nil or if the value they point to is empty
case reflect.Struct:
switch object.(type) {
case time.Time:
return object.(time.Time).IsZero()
}
case reflect.Ptr: case reflect.Ptr:
{
if objValue.IsNil() { if objValue.IsNil() {
return true return true
} }
switch object.(type) { deref := objValue.Elem().Interface()
case *time.Time: return isEmpty(deref)
return object.(*time.Time).IsZero() // for all other types, compare against the zero value
default: default:
return false zero := reflect.Zero(objValue.Type())
return reflect.DeepEqual(object, zero.Interface())
} }
} }
}
return false
}
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0. // a slice or a channel with len == 0.

View File

@ -806,6 +806,15 @@ func TestEmpty(t *testing.T) {
var tiNP time.Time var tiNP time.Time
var s *string var s *string
var f *os.File var f *os.File
sP := &s
x := 1
xP := &x
type TString string
type TStruct struct {
x int
s []int
}
True(t, Empty(mockT, ""), "Empty string is empty") True(t, Empty(mockT, ""), "Empty string is empty")
True(t, Empty(mockT, nil), "Nil is empty") True(t, Empty(mockT, nil), "Nil is empty")
@ -817,6 +826,9 @@ func TestEmpty(t *testing.T) {
True(t, Empty(mockT, f), "Nil os.File pointer is empty") True(t, Empty(mockT, f), "Nil os.File pointer is empty")
True(t, Empty(mockT, tiP), "Nil time.Time pointer is empty") True(t, Empty(mockT, tiP), "Nil time.Time pointer is empty")
True(t, Empty(mockT, tiNP), "time.Time is empty") True(t, Empty(mockT, tiNP), "time.Time is empty")
True(t, Empty(mockT, TStruct{}), "struct with zero values is empty")
True(t, Empty(mockT, TString("")), "empty aliased string is empty")
True(t, Empty(mockT, sP), "ptr to nil value is empty")
False(t, Empty(mockT, "something"), "Non Empty string is not 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") False(t, Empty(mockT, errors.New("something")), "Non nil object is not empty")
@ -824,6 +836,9 @@ func TestEmpty(t *testing.T) {
False(t, Empty(mockT, 1), "Non-zero int value is not empty") False(t, Empty(mockT, 1), "Non-zero int value is not empty")
False(t, Empty(mockT, true), "True value is not empty") False(t, Empty(mockT, true), "True value is not empty")
False(t, Empty(mockT, chWithValue), "Channel with values is not empty") False(t, Empty(mockT, chWithValue), "Channel with values is not empty")
False(t, Empty(mockT, TStruct{x: 1}), "struct with initialized values is empty")
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")
} }
func TestNotEmpty(t *testing.T) { func TestNotEmpty(t *testing.T) {