massert: make most assertions handle the case of a reference type, like a map, being changed before Assert is called

This commit is contained in:
Brian Picciano 2018-10-15 14:34:10 -04:00
parent bc8f323514
commit 23045168cf
2 changed files with 66 additions and 34 deletions

View File

@ -290,6 +290,10 @@ func toStr(i interface{}) string {
// Equal asserts that the two values are exactly equal, and uses the // Equal asserts that the two values are exactly equal, and uses the
// reflect.DeepEqual function to determine if they are. // reflect.DeepEqual function to determine if they are.
//
// TODO this does not currently handle the case of creating the Assertion using
// a reference type (like a map), changing one of the map's keys, and then
// calling Assert.
func Equal(a, b interface{}) Assertion { func Equal(a, b interface{}) Assertion {
return newAssertion(func() error { return newAssertion(func() error {
if !reflect.DeepEqual(a, b) { if !reflect.DeepEqual(a, b) {
@ -320,6 +324,10 @@ func Nil(i interface{}) Assertion {
}, toStr(i)+" is nil", 0) }, toStr(i)+" is nil", 0)
} }
type setKV struct {
k, v interface{}
}
func toSet(i interface{}, keyedMap bool) ([]interface{}, error) { func toSet(i interface{}, keyedMap bool) ([]interface{}, error) {
v := reflect.ValueOf(i) v := reflect.ValueOf(i)
switch v.Kind() { switch v.Kind() {
@ -334,7 +342,7 @@ func toSet(i interface{}, keyedMap bool) ([]interface{}, error) {
vv := make([]interface{}, len(keys)) vv := make([]interface{}, len(keys))
for i := range keys { for i := range keys {
if keyedMap { if keyedMap {
vv[i] = struct{ k, v interface{} }{ vv[i] = setKV{
k: keys[i].Interface(), k: keys[i].Interface(),
v: v.MapIndex(keys[i]).Interface(), v: v.MapIndex(keys[i]).Interface(),
} }
@ -351,20 +359,19 @@ func toSet(i interface{}, keyedMap bool) ([]interface{}, error) {
// Subset asserts that the given subset is a subset of the given set. Both must // Subset asserts that the given subset is a subset of the given set. Both must
// be of the same type and may be arrays, slices, or maps. // be of the same type and may be arrays, slices, or maps.
func Subset(set, subset interface{}) Assertion { func Subset(set, subset interface{}) Assertion {
if reflect.TypeOf(set) != reflect.TypeOf(subset) {
panic(errors.New("set and subset aren't of same type"))
}
setVV, err := toSet(set, true)
if err != nil {
panic(err)
}
subsetVV, err := toSet(subset, true)
if err != nil {
panic(err)
}
return newAssertion(func() error { return newAssertion(func() error {
if reflect.TypeOf(set) != reflect.TypeOf(subset) {
return errors.New("set and subset aren't of same type")
}
setVV, err := toSet(set, true)
if err != nil {
return err
}
subsetVV, err := toSet(subset, true)
if err != nil {
return err
}
// this is obviously not the most efficient way to do this // this is obviously not the most efficient way to do this
outer: outer:
for i := range subsetVV { for i := range subsetVV {
@ -383,12 +390,12 @@ func Subset(set, subset interface{}) Assertion {
// set may be an array, a slice, or a map, and if it's a map then the elem will // set may be an array, a slice, or a map, and if it's a map then the elem will
// need to be a value in it. // need to be a value in it.
func Has(set, elem interface{}) Assertion { func Has(set, elem interface{}) Assertion {
return newAssertion(func() error { setVV, err := toSet(set, false)
setVV, err := toSet(set, false) if err != nil {
if err != nil { panic(err)
return err }
}
return newAssertion(func() error {
for i := range setVV { for i := range setVV {
if reflect.DeepEqual(setVV[i], elem) { if reflect.DeepEqual(setVV[i], elem) {
return nil return nil
@ -401,14 +408,16 @@ func Has(set, elem interface{}) Assertion {
// HasKey asserts that the given set (which must be a map type) has the given // HasKey asserts that the given set (which must be a map type) has the given
// element as a key in it. // element as a key in it.
func HasKey(set, elem interface{}) Assertion { func HasKey(set, elem interface{}) Assertion {
if v := reflect.ValueOf(set); v.Kind() != reflect.Map {
panic(fmt.Errorf("type %s is not a map", v.Type()))
}
setVV, err := toSet(set, true)
if err != nil {
panic(err)
}
return newAssertion(func() error { return newAssertion(func() error {
v := reflect.ValueOf(set) for _, kv := range setVV {
if v.Kind() != reflect.Map { if reflect.DeepEqual(kv.(setKV).k, elem) {
return fmt.Errorf("type %s is not a map", v.Type())
}
for _, key := range v.MapKeys() {
if reflect.DeepEqual(key.Interface(), elem) {
return nil return nil
} }
} }
@ -420,11 +429,13 @@ func HasKey(set, elem interface{}) Assertion {
// set may be an array, a slice, or a map. A nil value'd set is considered to be // set may be an array, a slice, or a map. A nil value'd set is considered to be
// a length of zero. // a length of zero.
func Len(set interface{}, length int) Assertion { func Len(set interface{}, length int) Assertion {
setVV, err := toSet(set, false)
if err != nil {
panic(err)
}
return newAssertion(func() error { return newAssertion(func() error {
setVV, err := toSet(set, false) if len(setVV) != length {
if err != nil {
return err
} else if len(setVV) != length {
return fmt.Errorf("set not correct length, is %d", len(setVV)) return fmt.Errorf("set not correct length, is %d", len(setVV))
} }
return nil return nil

View File

@ -145,15 +145,19 @@ func TestSubset(t *T) {
)) ))
Fatal(t, None( Fatal(t, None(
Subset([]int64{1, 2, 3}, []int{1}),
Subset([]int{}, []int{1, 2, 3}), Subset([]int{}, []int{1, 2, 3}),
Subset([]int{1, 2, 3}, []int{4}), Subset([]int{1, 2, 3}, []int{4}),
Subset([]int{1, 2, 3}, []int{1, 3, 2, 4}), Subset([]int{1, 2, 3}, []int{1, 3, 2, 4}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int64{1: 1}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 2}), Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 2}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 1, 3: 3}), Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 1, 3: 3}),
)) ))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1, 2: 2}
a := Subset(m, map[int]int{1: 1})
m[1] = 2
Fatal(t, a)
} }
func TestHas(t *T) { func TestHas(t *T) {
@ -176,6 +180,12 @@ func TestHas(t *T) {
Has(map[int]int{1: 2}, 1), Has(map[int]int{1: 2}, 1),
Has(map[int]int{1: 2, 2: 1}, 3), Has(map[int]int{1: 2, 2: 1}, 3),
)) ))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1}
a := Has(m, 1)
m[1] = 2
Fatal(t, a)
} }
func TestHasKey(t *T) { func TestHasKey(t *T) {
@ -186,11 +196,16 @@ func TestHasKey(t *T) {
)) ))
Fatal(t, None( Fatal(t, None(
HasKey([]int{}, 1),
HasKey([]int{1}, 1),
HasKey(map[int]int{}, 1), HasKey(map[int]int{}, 1),
HasKey(map[int]int{2: 2}, 1), HasKey(map[int]int{2: 2}, 1),
)) ))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1}
a := HasKey(m, 1)
delete(m, 1)
Fatal(t, a)
} }
func TestLen(t *T) { func TestLen(t *T) {
@ -215,4 +230,10 @@ func TestLen(t *T) {
Len(map[int]int(nil), 1), Len(map[int]int(nil), 1),
Len(map[int]int{}, 1), Len(map[int]int{}, 1),
)) ))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1}
a := Len(m, 1)
m[2] = 2
Fatal(t, a)
} }