e2etest/newe2e_assertions.go (212 lines of code) (raw):
package e2etest
import (
"fmt"
"github.com/Azure/azure-storage-azcopy/v10/common"
"reflect"
"runtime/debug"
"strings"
)
var _ Assertion = NoError{}
var _ Assertion = IsNil{}
var _ Assertion = Not{}
var _ Assertion = Empty{}
var _ Assertion = Equal{}
var _ Assertion = MapContains[bool, bool]{}
var _ Assertion = Always{}
// ====== NoError ======
// NoError works like IsNil but only asserts when there is an error, and formats errors
type NoError struct {
// set stackTrace to true to get the panic stack trace
stackTrace bool
// todo: traced error decorations
}
func (n NoError) Name() string {
return "NoError"
}
func (n NoError) MaxArgs() int {
return 0
}
func (n NoError) MinArgs() int {
return 0
}
func (n NoError) Assert(items ...any) bool {
for _, v := range items {
if v != nil {
return false
}
}
return true
}
func (n NoError) Format(items ...any) string {
out := ""
for _, v := range items {
if err, ok := v.(error); ok {
out += err.Error() + "\n\n"
} else if v != nil {
out += fmt.Sprintf("item %s was not an error, but also not nil\n\n", v)
}
}
if n.stackTrace {
stack := debug.Stack()
out += string(stack)
}
strings.TrimSuffix(out, "\n\n")
return out
}
// ====== IsNil ======
// IsNil checks that all parameters are nil.
type IsNil struct{}
func (i IsNil) Name() string {
return "IsNil"
}
func (i IsNil) MaxArgs() int {
return 0
}
func (i IsNil) MinArgs() int {
return 0
}
func (i IsNil) Assert(items ...any) bool {
for _, v := range items {
if v != nil {
return false
}
}
return true
}
// ====== Not ======
// Not inverts the contained Assertion.
type Not struct {
a Assertion
}
func (n Not) Name() string {
return "Not(" + n.a.Name() + ")"
}
func (n Not) MaxArgs() int {
return n.a.MaxArgs()
}
func (n Not) MinArgs() int {
return n.a.MinArgs()
}
func (n Not) Assert(items ...any) bool {
return !n.a.Assert(items...)
}
// ====== Empty =======
// Empty checks that all parameters are equivalent to their zero-values
type Empty struct {
// Invert is distinctly different from Not{Empty{}}, in that Not{Empty{}} states "if *any* object is not empty", but Empty{Invert: true} specifies that ALL objects are nonzero
Invert bool
}
func (e Empty) Name() string {
return "IsEmpty"
}
func (e Empty) MaxArgs() int {
return 0
}
func (e Empty) MinArgs() int {
return 0
}
func (e Empty) Assert(items ...any) bool {
for _, v := range items {
item := reflect.ValueOf(v)
// false (all objects are zero) == false (the object is not zero); failure
// true (all objects are nonzero) == true (the object is zero); failure
if e.Invert == item.IsZero() {
return false
}
}
return true
}
func (e Empty) Format(items ...any) string {
failed := make([]uint, 0)
for idx, v := range items {
item := reflect.ValueOf(v)
// false (all objects are zero) == false (the object is not zero); failure
// true (all objects are nonzero) == true (the object is zero); failure
if e.Invert == item.IsZero() {
failed = append(failed, uint(idx))
}
}
if len(failed) == 0 {
return "all items were " + common.Iff(e.Invert, "nonzero", "zero")
}
trait := common.Iff(e.Invert, "zero, expected nonzero values", "nonzero, expected zero values")
return fmt.Sprintf("items %v were %s", failed, trait)
}
// ====== Equal =======
// Equal checks that all parameters are equal.
type Equal struct {
Deep bool
}
func (e Equal) Name() string {
return "Equal"
}
func (e Equal) MaxArgs() int {
return 0
}
func (e Equal) MinArgs() int {
return 0
}
func (e Equal) Assert(items ...any) bool {
if len(items) == 0 {
return true
}
left := items[0]
for _, right := range items[1:] {
if !e.Deep {
if left != right {
return false
}
} else {
if !reflect.DeepEqual(left, right) {
return false
}
}
}
return true
}
// ====== Contains ======
// Contains checks that all parameters are included within the array (or map's keys)
// MapContains takes in a TargetMap, and multiple KVPair objects, and checks if the map contains all of them.
type MapContains[K comparable, V any] struct {
TargetMap map[K]V
ValueToKVPair func(V) KVPair[K, V]
}
type KVPair[K comparable, V any] struct {
Key K
Value V
}
func (m MapContains[K, V]) Name() string {
return "MapContains"
}
func (m MapContains[K, V]) MaxArgs() int {
return 0
}
func (m MapContains[K, V]) MinArgs() int {
return 0
}
func (m MapContains[K, V]) Assert(items ...any) bool {
if (m.TargetMap == nil || len(m.TargetMap) == 0) && len(items) > 0 {
return false // Map is nil, so, can't contain anything!
}
for len(items) > 0 {
v := items[0]
items = items[1:]
kvPair, ok := v.(KVPair[K, V])
if !ok {
if val, ok := v.(V); ok {
kvPair = m.ValueToKVPair(val)
} else if dict, ok := v.(map[K]V); ok {
for key, value := range dict {
items = append(items, KVPair[K, V]{key, value})
}
continue
} else {
panic("MapContains only accepts KVPair[K,V], map[K]V, or V as items")
}
}
val, ok := m.TargetMap[kvPair.Key]
if !ok {
return false // map must contain the key
}
if !reflect.DeepEqual(val, kvPair.Value) {
return false
}
}
return true
}
// ====== Always ======
// Always asserts. It's designed to throw quick errors.
type Always struct{}
func (a Always) Name() string {
return ""
}
func (a Always) MaxArgs() int {
return 0
}
func (a Always) MinArgs() int {
return 0
}
func (a Always) Assert(items ...any) bool {
return false
}
func (a Always) Format(items ...any) string {
return ""
}