Update packages and add vendor directory

This commit is contained in:
Eddy Filip
2022-09-13 16:24:52 +03:00
parent 23b66f73af
commit 1d75f78891
3906 changed files with 1365387 additions and 465 deletions

62
vendor/github.com/onsi/gomega/matchers/and.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
type AndMatcher struct {
Matchers []types.GomegaMatcher
// state
firstFailedMatcher types.GomegaMatcher
}
func (m *AndMatcher) Match(actual interface{}) (success bool, err error) {
m.firstFailedMatcher = nil
for _, matcher := range m.Matchers {
success, err := matcher.Match(actual)
if !success || err != nil {
m.firstFailedMatcher = matcher
return false, err
}
}
return true, nil
}
func (m *AndMatcher) FailureMessage(actual interface{}) (message string) {
return m.firstFailedMatcher.FailureMessage(actual)
}
func (m *AndMatcher) NegatedFailureMessage(actual interface{}) (message string) {
// not the most beautiful list of matchers, but not bad either...
return format.Message(actual, fmt.Sprintf("To not satisfy all of these matchers: %s", m.Matchers))
}
func (m *AndMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
/*
Example with 3 matchers: A, B, C
Match evaluates them: T, F, <?> => F
So match is currently F, what should MatchMayChangeInTheFuture() return?
Seems like it only depends on B, since currently B MUST change to allow the result to become T
Match eval: T, T, T => T
So match is currently T, what should MatchMayChangeInTheFuture() return?
Seems to depend on ANY of them being able to change to F.
*/
if m.firstFailedMatcher == nil {
// so all matchers succeeded.. Any one of them changing would change the result.
for _, matcher := range m.Matchers {
if types.MatchMayChangeInTheFuture(matcher, actual) {
return true
}
}
return false // none of were going to change
}
// one of the matchers failed.. it must be able to change in order to affect the result
return types.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual)
}

View File

@@ -0,0 +1,37 @@
// untested sections: 2
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type AssignableToTypeOfMatcher struct {
Expected interface{}
}
func (matcher *AssignableToTypeOfMatcher) Match(actual interface{}) (success bool, err error) {
if actual == nil && matcher.Expected == nil {
return false, fmt.Errorf("Refusing to compare <nil> to <nil>.\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.")
} else if matcher.Expected == nil {
return false, fmt.Errorf("Refusing to compare type to <nil>.\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.")
} else if actual == nil {
return false, nil
}
actualType := reflect.TypeOf(actual)
expectedType := reflect.TypeOf(matcher.Expected)
return actualType.AssignableTo(expectedType), nil
}
func (matcher *AssignableToTypeOfMatcher) FailureMessage(actual interface{}) string {
return format.Message(actual, fmt.Sprintf("to be assignable to the type: %T", matcher.Expected))
}
func (matcher *AssignableToTypeOfMatcher) NegatedFailureMessage(actual interface{}) string {
return format.Message(actual, fmt.Sprintf("not to be assignable to the type: %T", matcher.Expected))
}

View File

@@ -0,0 +1,14 @@
package matchers
import (
"encoding/xml"
"strings"
)
type attributesSlice []xml.Attr
func (attrs attributesSlice) Len() int { return len(attrs) }
func (attrs attributesSlice) Less(i, j int) bool {
return strings.Compare(attrs[i].Name.Local, attrs[j].Name.Local) == -1
}
func (attrs attributesSlice) Swap(i, j int) { attrs[i], attrs[j] = attrs[j], attrs[i] }

View File

@@ -0,0 +1,56 @@
// untested sections: 5
package matchers
import (
"fmt"
"os"
"github.com/onsi/gomega/format"
)
type notADirectoryError struct {
os.FileInfo
}
func (t notADirectoryError) Error() string {
fileInfo := os.FileInfo(t)
switch {
case fileInfo.Mode().IsRegular():
return "file is a regular file"
default:
return fmt.Sprintf("file mode is: %s", fileInfo.Mode().String())
}
}
type BeADirectoryMatcher struct {
expected interface{}
err error
}
func (matcher *BeADirectoryMatcher) Match(actual interface{}) (success bool, err error) {
actualFilename, ok := actual.(string)
if !ok {
return false, fmt.Errorf("BeADirectoryMatcher matcher expects a file path")
}
fileInfo, err := os.Stat(actualFilename)
if err != nil {
matcher.err = err
return false, nil
}
if !fileInfo.Mode().IsDir() {
matcher.err = notADirectoryError{fileInfo}
return false, nil
}
return true, nil
}
func (matcher *BeADirectoryMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("to be a directory: %s", matcher.err))
}
func (matcher *BeADirectoryMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("not be a directory"))
}

View File

@@ -0,0 +1,56 @@
// untested sections: 5
package matchers
import (
"fmt"
"os"
"github.com/onsi/gomega/format"
)
type notARegularFileError struct {
os.FileInfo
}
func (t notARegularFileError) Error() string {
fileInfo := os.FileInfo(t)
switch {
case fileInfo.IsDir():
return "file is a directory"
default:
return fmt.Sprintf("file mode is: %s", fileInfo.Mode().String())
}
}
type BeARegularFileMatcher struct {
expected interface{}
err error
}
func (matcher *BeARegularFileMatcher) Match(actual interface{}) (success bool, err error) {
actualFilename, ok := actual.(string)
if !ok {
return false, fmt.Errorf("BeARegularFileMatcher matcher expects a file path")
}
fileInfo, err := os.Stat(actualFilename)
if err != nil {
matcher.err = err
return false, nil
}
if !fileInfo.Mode().IsRegular() {
matcher.err = notARegularFileError{fileInfo}
return false, nil
}
return true, nil
}
func (matcher *BeARegularFileMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("to be a regular file: %s", matcher.err))
}
func (matcher *BeARegularFileMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("not be a regular file"))
}

View File

@@ -0,0 +1,40 @@
// untested sections: 3
package matchers
import (
"fmt"
"os"
"github.com/onsi/gomega/format"
)
type BeAnExistingFileMatcher struct {
expected interface{}
}
func (matcher *BeAnExistingFileMatcher) Match(actual interface{}) (success bool, err error) {
actualFilename, ok := actual.(string)
if !ok {
return false, fmt.Errorf("BeAnExistingFileMatcher matcher expects a file path")
}
if _, err = os.Stat(actualFilename); err != nil {
switch {
case os.IsNotExist(err):
return false, nil
default:
return false, err
}
}
return true, nil
}
func (matcher *BeAnExistingFileMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("to exist"))
}
func (matcher *BeAnExistingFileMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("not to exist"))
}

View File

@@ -0,0 +1,48 @@
// untested sections: 2
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type BeClosedMatcher struct {
}
func (matcher *BeClosedMatcher) Match(actual interface{}) (success bool, err error) {
if !isChan(actual) {
return false, fmt.Errorf("BeClosed matcher expects a channel. Got:\n%s", format.Object(actual, 1))
}
channelType := reflect.TypeOf(actual)
channelValue := reflect.ValueOf(actual)
if channelType.ChanDir() == reflect.SendDir {
return false, fmt.Errorf("BeClosed matcher cannot determine if a send-only channel is closed or open. Got:\n%s", format.Object(actual, 1))
}
winnerIndex, _, open := reflect.Select([]reflect.SelectCase{
{Dir: reflect.SelectRecv, Chan: channelValue},
{Dir: reflect.SelectDefault},
})
var closed bool
if winnerIndex == 0 {
closed = !open
} else if winnerIndex == 1 {
closed = false
}
return closed, nil
}
func (matcher *BeClosedMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be closed")
}
func (matcher *BeClosedMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be open")
}

View File

@@ -0,0 +1,43 @@
// untested sections: 1
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type BeElementOfMatcher struct {
Elements []interface{}
}
func (matcher *BeElementOfMatcher) Match(actual interface{}) (success bool, err error) {
if reflect.TypeOf(actual) == nil {
return false, fmt.Errorf("BeElement matcher expects actual to be typed")
}
var lastError error
for _, m := range flatten(matcher.Elements) {
matcher := &EqualMatcher{Expected: m}
success, err := matcher.Match(actual)
if err != nil {
lastError = err
continue
}
if success {
return true, nil
}
}
return false, lastError
}
func (matcher *BeElementOfMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be an element of", presentable(matcher.Elements))
}
func (matcher *BeElementOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be an element of", presentable(matcher.Elements))
}

View File

@@ -0,0 +1,29 @@
// untested sections: 2
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type BeEmptyMatcher struct {
}
func (matcher *BeEmptyMatcher) Match(actual interface{}) (success bool, err error) {
length, ok := lengthOf(actual)
if !ok {
return false, fmt.Errorf("BeEmpty matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1))
}
return length == 0, nil
}
func (matcher *BeEmptyMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be empty")
}
func (matcher *BeEmptyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be empty")
}

View File

@@ -0,0 +1,36 @@
// untested sections: 2
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type BeEquivalentToMatcher struct {
Expected interface{}
}
func (matcher *BeEquivalentToMatcher) Match(actual interface{}) (success bool, err error) {
if actual == nil && matcher.Expected == nil {
return false, fmt.Errorf("Both actual and expected must not be nil.")
}
convertedActual := actual
if actual != nil && matcher.Expected != nil && reflect.TypeOf(actual).ConvertibleTo(reflect.TypeOf(matcher.Expected)) {
convertedActual = reflect.ValueOf(actual).Convert(reflect.TypeOf(matcher.Expected)).Interface()
}
return reflect.DeepEqual(convertedActual, matcher.Expected), nil
}
func (matcher *BeEquivalentToMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be equivalent to", matcher.Expected)
}
func (matcher *BeEquivalentToMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be equivalent to", matcher.Expected)
}

View File

@@ -0,0 +1,28 @@
// untested sections: 2
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type BeFalseMatcher struct {
}
func (matcher *BeFalseMatcher) Match(actual interface{}) (success bool, err error) {
if !isBool(actual) {
return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1))
}
return actual == false, nil
}
func (matcher *BeFalseMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be false")
}
func (matcher *BeFalseMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be false")
}

View File

@@ -0,0 +1,39 @@
// untested sections: 2
package matchers
import (
"fmt"
"runtime"
"github.com/onsi/gomega/format"
)
type BeIdenticalToMatcher struct {
Expected interface{}
}
func (matcher *BeIdenticalToMatcher) Match(actual interface{}) (success bool, matchErr error) {
if actual == nil && matcher.Expected == nil {
return false, fmt.Errorf("Refusing to compare <nil> to <nil>.\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.")
}
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
success = false
matchErr = nil
}
}
}()
return actual == matcher.Expected, nil
}
func (matcher *BeIdenticalToMatcher) FailureMessage(actual interface{}) string {
return format.Message(actual, "to be identical to", matcher.Expected)
}
func (matcher *BeIdenticalToMatcher) NegatedFailureMessage(actual interface{}) string {
return format.Message(actual, "not to be identical to", matcher.Expected)
}

View File

@@ -0,0 +1,20 @@
// untested sections: 2
package matchers
import "github.com/onsi/gomega/format"
type BeNilMatcher struct {
}
func (matcher *BeNilMatcher) Match(actual interface{}) (success bool, err error) {
return isNil(actual), nil
}
func (matcher *BeNilMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be nil")
}
func (matcher *BeNilMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be nil")
}

View File

@@ -0,0 +1,134 @@
// untested sections: 4
package matchers
import (
"fmt"
"math"
"github.com/onsi/gomega/format"
)
type BeNumericallyMatcher struct {
Comparator string
CompareTo []interface{}
}
func (matcher *BeNumericallyMatcher) FailureMessage(actual interface{}) (message string) {
return matcher.FormatFailureMessage(actual, false)
}
func (matcher *BeNumericallyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return matcher.FormatFailureMessage(actual, true)
}
func (matcher *BeNumericallyMatcher) FormatFailureMessage(actual interface{}, negated bool) (message string) {
if len(matcher.CompareTo) == 1 {
message = fmt.Sprintf("to be %s", matcher.Comparator)
} else {
message = fmt.Sprintf("to be within %v of %s", matcher.CompareTo[1], matcher.Comparator)
}
if negated {
message = "not " + message
}
return format.Message(actual, message, matcher.CompareTo[0])
}
func (matcher *BeNumericallyMatcher) Match(actual interface{}) (success bool, err error) {
if len(matcher.CompareTo) == 0 || len(matcher.CompareTo) > 2 {
return false, fmt.Errorf("BeNumerically requires 1 or 2 CompareTo arguments. Got:\n%s", format.Object(matcher.CompareTo, 1))
}
if !isNumber(actual) {
return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(actual, 1))
}
if !isNumber(matcher.CompareTo[0]) {
return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1))
}
if len(matcher.CompareTo) == 2 && !isNumber(matcher.CompareTo[1]) {
return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[1], 1))
}
switch matcher.Comparator {
case "==", "~", ">", ">=", "<", "<=":
default:
return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator)
}
if isFloat(actual) || isFloat(matcher.CompareTo[0]) {
var secondOperand float64 = 1e-8
if len(matcher.CompareTo) == 2 {
secondOperand = toFloat(matcher.CompareTo[1])
}
success = matcher.matchFloats(toFloat(actual), toFloat(matcher.CompareTo[0]), secondOperand)
} else if isInteger(actual) {
var secondOperand int64 = 0
if len(matcher.CompareTo) == 2 {
secondOperand = toInteger(matcher.CompareTo[1])
}
success = matcher.matchIntegers(toInteger(actual), toInteger(matcher.CompareTo[0]), secondOperand)
} else if isUnsignedInteger(actual) {
var secondOperand uint64 = 0
if len(matcher.CompareTo) == 2 {
secondOperand = toUnsignedInteger(matcher.CompareTo[1])
}
success = matcher.matchUnsignedIntegers(toUnsignedInteger(actual), toUnsignedInteger(matcher.CompareTo[0]), secondOperand)
} else {
return false, fmt.Errorf("Failed to compare:\n%s\n%s:\n%s", format.Object(actual, 1), matcher.Comparator, format.Object(matcher.CompareTo[0], 1))
}
return success, nil
}
func (matcher *BeNumericallyMatcher) matchIntegers(actual, compareTo, threshold int64) (success bool) {
switch matcher.Comparator {
case "==", "~":
diff := actual - compareTo
return -threshold <= diff && diff <= threshold
case ">":
return (actual > compareTo)
case ">=":
return (actual >= compareTo)
case "<":
return (actual < compareTo)
case "<=":
return (actual <= compareTo)
}
return false
}
func (matcher *BeNumericallyMatcher) matchUnsignedIntegers(actual, compareTo, threshold uint64) (success bool) {
switch matcher.Comparator {
case "==", "~":
if actual < compareTo {
actual, compareTo = compareTo, actual
}
return actual-compareTo <= threshold
case ">":
return (actual > compareTo)
case ">=":
return (actual >= compareTo)
case "<":
return (actual < compareTo)
case "<=":
return (actual <= compareTo)
}
return false
}
func (matcher *BeNumericallyMatcher) matchFloats(actual, compareTo, threshold float64) (success bool) {
switch matcher.Comparator {
case "~":
return math.Abs(actual-compareTo) <= threshold
case "==":
return (actual == compareTo)
case ">":
return (actual > compareTo)
case ">=":
return (actual >= compareTo)
case "<":
return (actual < compareTo)
case "<=":
return (actual <= compareTo)
}
return false
}

View File

@@ -0,0 +1,73 @@
// untested sections: 3
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type BeSentMatcher struct {
Arg interface{}
channelClosed bool
}
func (matcher *BeSentMatcher) Match(actual interface{}) (success bool, err error) {
if !isChan(actual) {
return false, fmt.Errorf("BeSent expects a channel. Got:\n%s", format.Object(actual, 1))
}
channelType := reflect.TypeOf(actual)
channelValue := reflect.ValueOf(actual)
if channelType.ChanDir() == reflect.RecvDir {
return false, fmt.Errorf("BeSent matcher cannot be passed a receive-only channel. Got:\n%s", format.Object(actual, 1))
}
argType := reflect.TypeOf(matcher.Arg)
assignable := argType.AssignableTo(channelType.Elem())
if !assignable {
return false, fmt.Errorf("Cannot pass:\n%s to the channel:\n%s\nThe types don't match.", format.Object(matcher.Arg, 1), format.Object(actual, 1))
}
argValue := reflect.ValueOf(matcher.Arg)
defer func() {
if e := recover(); e != nil {
success = false
err = fmt.Errorf("Cannot send to a closed channel")
matcher.channelClosed = true
}
}()
winnerIndex, _, _ := reflect.Select([]reflect.SelectCase{
{Dir: reflect.SelectSend, Chan: channelValue, Send: argValue},
{Dir: reflect.SelectDefault},
})
var didSend bool
if winnerIndex == 0 {
didSend = true
}
return didSend, nil
}
func (matcher *BeSentMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to send:", matcher.Arg)
}
func (matcher *BeSentMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to send:", matcher.Arg)
}
func (matcher *BeSentMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
if !isChan(actual) {
return false
}
return !matcher.channelClosed
}

View File

@@ -0,0 +1,68 @@
// untested sections: 3
package matchers
import (
"fmt"
"time"
"github.com/onsi/gomega/format"
)
type BeTemporallyMatcher struct {
Comparator string
CompareTo time.Time
Threshold []time.Duration
}
func (matcher *BeTemporallyMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("to be %s", matcher.Comparator), matcher.CompareTo)
}
func (matcher *BeTemporallyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Comparator), matcher.CompareTo)
}
func (matcher *BeTemporallyMatcher) Match(actual interface{}) (bool, error) {
// predicate to test for time.Time type
isTime := func(t interface{}) bool {
_, ok := t.(time.Time)
return ok
}
if !isTime(actual) {
return false, fmt.Errorf("Expected a time.Time. Got:\n%s", format.Object(actual, 1))
}
switch matcher.Comparator {
case "==", "~", ">", ">=", "<", "<=":
default:
return false, fmt.Errorf("Unknown comparator: %s", matcher.Comparator)
}
var threshold = time.Millisecond
if len(matcher.Threshold) == 1 {
threshold = matcher.Threshold[0]
}
return matcher.matchTimes(actual.(time.Time), matcher.CompareTo, threshold), nil
}
func (matcher *BeTemporallyMatcher) matchTimes(actual, compareTo time.Time, threshold time.Duration) (success bool) {
switch matcher.Comparator {
case "==":
return actual.Equal(compareTo)
case "~":
diff := actual.Sub(compareTo)
return -threshold <= diff && diff <= threshold
case ">":
return actual.After(compareTo)
case ">=":
return !actual.Before(compareTo)
case "<":
return actual.Before(compareTo)
case "<=":
return !actual.After(compareTo)
}
return false
}

View File

@@ -0,0 +1,28 @@
// untested sections: 2
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type BeTrueMatcher struct {
}
func (matcher *BeTrueMatcher) Match(actual interface{}) (success bool, err error) {
if !isBool(actual) {
return false, fmt.Errorf("Expected a boolean. Got:\n%s", format.Object(actual, 1))
}
return actual.(bool), nil
}
func (matcher *BeTrueMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be true")
}
func (matcher *BeTrueMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be true")
}

View File

@@ -0,0 +1,28 @@
package matchers
import (
"reflect"
"github.com/onsi/gomega/format"
)
type BeZeroMatcher struct {
}
func (matcher *BeZeroMatcher) Match(actual interface{}) (success bool, err error) {
if actual == nil {
return true, nil
}
zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface()
return reflect.DeepEqual(zeroValue, actual), nil
}
func (matcher *BeZeroMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be zero-valued")
}
func (matcher *BeZeroMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be zero-valued")
}

144
vendor/github.com/onsi/gomega/matchers/consist_of.go generated vendored Normal file
View File

@@ -0,0 +1,144 @@
// untested sections: 3
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/matchers/support/goraph/bipartitegraph"
)
type ConsistOfMatcher struct {
Elements []interface{}
missingElements []interface{}
extraElements []interface{}
}
func (matcher *ConsistOfMatcher) Match(actual interface{}) (success bool, err error) {
if !isArrayOrSlice(actual) && !isMap(actual) {
return false, fmt.Errorf("ConsistOf matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1))
}
matchers := matchers(matcher.Elements)
values := valuesOf(actual)
bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(values, matchers, neighbours)
if err != nil {
return false, err
}
edges := bipartiteGraph.LargestMatching()
if len(edges) == len(values) && len(edges) == len(matchers) {
return true, nil
}
var missingMatchers []interface{}
matcher.extraElements, missingMatchers = bipartiteGraph.FreeLeftRight(edges)
matcher.missingElements = equalMatchersToElements(missingMatchers)
return false, nil
}
func neighbours(value, matcher interface{}) (bool, error) {
match, err := matcher.(omegaMatcher).Match(value)
return match && err == nil, nil
}
func equalMatchersToElements(matchers []interface{}) (elements []interface{}) {
for _, matcher := range matchers {
equalMatcher, ok := matcher.(*EqualMatcher)
if ok {
matcher = equalMatcher.Expected
}
elements = append(elements, matcher)
}
return
}
func flatten(elems []interface{}) []interface{} {
if len(elems) != 1 || !isArrayOrSlice(elems[0]) {
return elems
}
value := reflect.ValueOf(elems[0])
flattened := make([]interface{}, value.Len())
for i := 0; i < value.Len(); i++ {
flattened[i] = value.Index(i).Interface()
}
return flattened
}
func matchers(expectedElems []interface{}) (matchers []interface{}) {
for _, e := range flatten(expectedElems) {
matcher, isMatcher := e.(omegaMatcher)
if !isMatcher {
matcher = &EqualMatcher{Expected: e}
}
matchers = append(matchers, matcher)
}
return
}
func presentable(elems []interface{}) interface{} {
elems = flatten(elems)
if len(elems) == 0 {
return []interface{}{}
}
sv := reflect.ValueOf(elems)
tt := sv.Index(0).Elem().Type()
for i := 1; i < sv.Len(); i++ {
if sv.Index(i).Elem().Type() != tt {
return elems
}
}
ss := reflect.MakeSlice(reflect.SliceOf(tt), sv.Len(), sv.Len())
for i := 0; i < sv.Len(); i++ {
ss.Index(i).Set(sv.Index(i).Elem())
}
return ss.Interface()
}
func valuesOf(actual interface{}) []interface{} {
value := reflect.ValueOf(actual)
values := []interface{}{}
if isMap(actual) {
keys := value.MapKeys()
for i := 0; i < value.Len(); i++ {
values = append(values, value.MapIndex(keys[i]).Interface())
}
} else {
for i := 0; i < value.Len(); i++ {
values = append(values, value.Index(i).Interface())
}
}
return values
}
func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) {
message = format.Message(actual, "to consist of", presentable(matcher.Elements))
message = appendMissingElements(message, matcher.missingElements)
if len(matcher.extraElements) > 0 {
message = fmt.Sprintf("%s\nthe extra elements were\n%s", message,
format.Object(presentable(matcher.extraElements), 1))
}
return
}
func appendMissingElements(message string, missingElements []interface{}) string {
if len(missingElements) == 0 {
return message
}
return fmt.Sprintf("%s\nthe missing elements were\n%s", message,
format.Object(presentable(missingElements), 1))
}
func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to consist of", presentable(matcher.Elements))
}

View File

@@ -0,0 +1,174 @@
// untested sections: 2
package matchers
import (
"errors"
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type ContainElementMatcher struct {
Element interface{}
Result []interface{}
}
func (matcher *ContainElementMatcher) Match(actual interface{}) (success bool, err error) {
if !isArrayOrSlice(actual) && !isMap(actual) {
return false, fmt.Errorf("ContainElement matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1))
}
var actualT reflect.Type
var result reflect.Value
switch l := len(matcher.Result); {
case l > 1:
return false, errors.New("ContainElement matcher expects at most a single optional pointer to store its findings at")
case l == 1:
if reflect.ValueOf(matcher.Result[0]).Kind() != reflect.Ptr {
return false, fmt.Errorf("ContainElement matcher expects a non-nil pointer to store its findings at. Got\n%s",
format.Object(matcher.Result[0], 1))
}
actualT = reflect.TypeOf(actual)
resultReference := matcher.Result[0]
result = reflect.ValueOf(resultReference).Elem() // what ResultReference points to, to stash away our findings
switch result.Kind() {
case reflect.Array:
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
reflect.SliceOf(actualT.Elem()).String(), result.Type().String())
case reflect.Slice:
if !isArrayOrSlice(actual) {
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
reflect.MapOf(actualT.Key(), actualT.Elem()).String(), result.Type().String())
}
if !actualT.Elem().AssignableTo(result.Type().Elem()) {
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
actualT.String(), result.Type().String())
}
case reflect.Map:
if !isMap(actual) {
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
actualT.String(), result.Type().String())
}
if !actualT.AssignableTo(result.Type()) {
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
actualT.String(), result.Type().String())
}
default:
if !actualT.Elem().AssignableTo(result.Type()) {
return false, fmt.Errorf("ContainElement cannot return findings. Need *%s, got *%s",
actualT.Elem().String(), result.Type().String())
}
}
}
elemMatcher, elementIsMatcher := matcher.Element.(omegaMatcher)
if !elementIsMatcher {
elemMatcher = &EqualMatcher{Expected: matcher.Element}
}
value := reflect.ValueOf(actual)
var valueAt func(int) interface{}
var getFindings func() reflect.Value
var foundAt func(int)
if isMap(actual) {
keys := value.MapKeys()
valueAt = func(i int) interface{} {
return value.MapIndex(keys[i]).Interface()
}
if result.Kind() != reflect.Invalid {
fm := reflect.MakeMap(actualT)
getFindings = func() reflect.Value {
return fm
}
foundAt = func(i int) {
fm.SetMapIndex(keys[i], value.MapIndex(keys[i]))
}
}
} else {
valueAt = func(i int) interface{} {
return value.Index(i).Interface()
}
if result.Kind() != reflect.Invalid {
var f reflect.Value
if result.Kind() == reflect.Slice {
f = reflect.MakeSlice(result.Type(), 0, 0)
} else {
f = reflect.MakeSlice(reflect.SliceOf(result.Type()), 0, 0)
}
getFindings = func() reflect.Value {
return f
}
foundAt = func(i int) {
f = reflect.Append(f, value.Index(i))
}
}
}
var lastError error
for i := 0; i < value.Len(); i++ {
elem := valueAt(i)
success, err := elemMatcher.Match(elem)
if err != nil {
lastError = err
continue
}
if success {
if result.Kind() == reflect.Invalid {
return true, nil
}
foundAt(i)
}
}
// when the expectation isn't interested in the findings except for success
// or non-success, then we're done here and return the last matcher error
// seen, if any, as well as non-success.
if result.Kind() == reflect.Invalid {
return false, lastError
}
// pick up any findings the test is interested in as it specified a non-nil
// result reference. However, the expection always is that there are at
// least one or multiple findings. So, if a result is expected, but we had
// no findings, then this is an error.
findings := getFindings()
if findings.Len() == 0 {
return false, lastError
}
// there's just a single finding and the result is neither a slice nor a map
// (so it's a scalar): pick the one and only finding and return it in the
// place the reference points to.
if findings.Len() == 1 && !isArrayOrSlice(result.Interface()) && !isMap(result.Interface()) {
if isMap(actual) {
miter := findings.MapRange()
miter.Next()
result.Set(miter.Value())
} else {
result.Set(findings.Index(0))
}
return true, nil
}
// at least one or even multiple findings and a the result references a
// slice or a map, so all we need to do is to store our findings where the
// reference points to.
if !findings.Type().AssignableTo(result.Type()) {
return false, fmt.Errorf("ContainElement cannot return multiple findings. Need *%s, got *%s",
findings.Type().String(), result.Type().String())
}
result.Set(findings)
return true, nil
}
func (matcher *ContainElementMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to contain element matching", matcher.Element)
}
func (matcher *ContainElementMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to contain element matching", matcher.Element)
}

View File

@@ -0,0 +1,44 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/matchers/support/goraph/bipartitegraph"
)
type ContainElementsMatcher struct {
Elements []interface{}
missingElements []interface{}
}
func (matcher *ContainElementsMatcher) Match(actual interface{}) (success bool, err error) {
if !isArrayOrSlice(actual) && !isMap(actual) {
return false, fmt.Errorf("ContainElements matcher expects an array/slice/map. Got:\n%s", format.Object(actual, 1))
}
matchers := matchers(matcher.Elements)
bipartiteGraph, err := bipartitegraph.NewBipartiteGraph(valuesOf(actual), matchers, neighbours)
if err != nil {
return false, err
}
edges := bipartiteGraph.LargestMatching()
if len(edges) == len(matchers) {
return true, nil
}
_, missingMatchers := bipartiteGraph.FreeLeftRight(edges)
matcher.missingElements = equalMatchersToElements(missingMatchers)
return false, nil
}
func (matcher *ContainElementsMatcher) FailureMessage(actual interface{}) (message string) {
message = format.Message(actual, "to contain elements", presentable(matcher.Elements))
return appendMissingElements(message, matcher.missingElements)
}
func (matcher *ContainElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to contain elements", presentable(matcher.Elements))
}

View File

@@ -0,0 +1,40 @@
// untested sections: 2
package matchers
import (
"fmt"
"strings"
"github.com/onsi/gomega/format"
)
type ContainSubstringMatcher struct {
Substr string
Args []interface{}
}
func (matcher *ContainSubstringMatcher) Match(actual interface{}) (success bool, err error) {
actualString, ok := toString(actual)
if !ok {
return false, fmt.Errorf("ContainSubstring matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1))
}
return strings.Contains(actualString, matcher.stringToMatch()), nil
}
func (matcher *ContainSubstringMatcher) stringToMatch() string {
stringToMatch := matcher.Substr
if len(matcher.Args) > 0 {
stringToMatch = fmt.Sprintf(matcher.Substr, matcher.Args...)
}
return stringToMatch
}
func (matcher *ContainSubstringMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to contain substring", matcher.stringToMatch())
}
func (matcher *ContainSubstringMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to contain substring", matcher.stringToMatch())
}

View File

@@ -0,0 +1,42 @@
package matchers
import (
"bytes"
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type EqualMatcher struct {
Expected interface{}
}
func (matcher *EqualMatcher) Match(actual interface{}) (success bool, err error) {
if actual == nil && matcher.Expected == nil {
return false, fmt.Errorf("Refusing to compare <nil> to <nil>.\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.")
}
// Shortcut for byte slices.
// Comparing long byte slices with reflect.DeepEqual is very slow,
// so use bytes.Equal if actual and expected are both byte slices.
if actualByteSlice, ok := actual.([]byte); ok {
if expectedByteSlice, ok := matcher.Expected.([]byte); ok {
return bytes.Equal(actualByteSlice, expectedByteSlice), nil
}
}
return reflect.DeepEqual(actual, matcher.Expected), nil
}
func (matcher *EqualMatcher) FailureMessage(actual interface{}) (message string) {
actualString, actualOK := actual.(string)
expectedString, expectedOK := matcher.Expected.(string)
if actualOK && expectedOK {
return format.MessageWithDiff(actualString, "to equal", expectedString)
}
return format.Message(actual, "to equal", matcher.Expected)
}
func (matcher *EqualMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to equal", matcher.Expected)
}

View File

@@ -0,0 +1,30 @@
// untested sections: 2
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type HaveCapMatcher struct {
Count int
}
func (matcher *HaveCapMatcher) Match(actual interface{}) (success bool, err error) {
length, ok := capOf(actual)
if !ok {
return false, fmt.Errorf("HaveCap matcher expects a array/channel/slice. Got:\n%s", format.Object(actual, 1))
}
return length == matcher.Count, nil
}
func (matcher *HaveCapMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n%s\nto have capacity %d", format.Object(actual, 1), matcher.Count)
}
func (matcher *HaveCapMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n%s\nnot to have capacity %d", format.Object(actual, 1), matcher.Count)
}

View File

@@ -0,0 +1,65 @@
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type HaveEachMatcher struct {
Element interface{}
}
func (matcher *HaveEachMatcher) Match(actual interface{}) (success bool, err error) {
if !isArrayOrSlice(actual) && !isMap(actual) {
return false, fmt.Errorf("HaveEach matcher expects an array/slice/map. Got:\n%s",
format.Object(actual, 1))
}
elemMatcher, elementIsMatcher := matcher.Element.(omegaMatcher)
if !elementIsMatcher {
elemMatcher = &EqualMatcher{Expected: matcher.Element}
}
value := reflect.ValueOf(actual)
if value.Len() == 0 {
return false, fmt.Errorf("HaveEach matcher expects a non-empty array/slice/map. Got:\n%s",
format.Object(actual, 1))
}
var valueAt func(int) interface{}
if isMap(actual) {
keys := value.MapKeys()
valueAt = func(i int) interface{} {
return value.MapIndex(keys[i]).Interface()
}
} else {
valueAt = func(i int) interface{} {
return value.Index(i).Interface()
}
}
// if there are no elements, then HaveEach will match.
for i := 0; i < value.Len(); i++ {
success, err := elemMatcher.Match(valueAt(i))
if err != nil {
return false, err
}
if !success {
return false, nil
}
}
return true, nil
}
// FailureMessage returns a suitable failure message.
func (matcher *HaveEachMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to contain element matching", matcher.Element)
}
// NegatedFailureMessage returns a suitable negated failure message.
func (matcher *HaveEachMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to contain element matching", matcher.Element)
}

87
vendor/github.com/onsi/gomega/matchers/have_field.go generated vendored Normal file
View File

@@ -0,0 +1,87 @@
package matchers
import (
"fmt"
"reflect"
"strings"
"github.com/onsi/gomega/format"
)
func extractField(actual interface{}, field string) (interface{}, error) {
fields := strings.SplitN(field, ".", 2)
actualValue := reflect.ValueOf(actual)
if actualValue.Kind() == reflect.Ptr {
actualValue = actualValue.Elem()
}
if actualValue == (reflect.Value{}) {
return nil, fmt.Errorf("HaveField encountered nil while dereferencing a pointer of type %T.", actual)
}
if actualValue.Kind() != reflect.Struct {
return nil, fmt.Errorf("HaveField encountered:\n%s\nWhich is not a struct.", format.Object(actual, 1))
}
var extractedValue reflect.Value
if strings.HasSuffix(fields[0], "()") {
extractedValue = actualValue.MethodByName(strings.TrimSuffix(fields[0], "()"))
if extractedValue == (reflect.Value{}) {
return nil, fmt.Errorf("HaveField could not find method named '%s' in struct of type %T.", fields[0], actual)
}
t := extractedValue.Type()
if t.NumIn() != 0 || t.NumOut() != 1 {
return nil, fmt.Errorf("HaveField found an invalid method named '%s' in struct of type %T.\nMethods must take no arguments and return exactly one value.", fields[0], actual)
}
extractedValue = extractedValue.Call([]reflect.Value{})[0]
} else {
extractedValue = actualValue.FieldByName(fields[0])
if extractedValue == (reflect.Value{}) {
return nil, fmt.Errorf("HaveField could not find field named '%s' in struct:\n%s", fields[0], format.Object(actual, 1))
}
}
if len(fields) == 1 {
return extractedValue.Interface(), nil
} else {
return extractField(extractedValue.Interface(), fields[1])
}
}
type HaveFieldMatcher struct {
Field string
Expected interface{}
extractedField interface{}
expectedMatcher omegaMatcher
}
func (matcher *HaveFieldMatcher) Match(actual interface{}) (success bool, err error) {
matcher.extractedField, err = extractField(actual, matcher.Field)
if err != nil {
return false, err
}
var isMatcher bool
matcher.expectedMatcher, isMatcher = matcher.Expected.(omegaMatcher)
if !isMatcher {
matcher.expectedMatcher = &EqualMatcher{Expected: matcher.Expected}
}
return matcher.expectedMatcher.Match(matcher.extractedField)
}
func (matcher *HaveFieldMatcher) FailureMessage(actual interface{}) (message string) {
message = fmt.Sprintf("Value for field '%s' failed to satisfy matcher.\n", matcher.Field)
message += matcher.expectedMatcher.FailureMessage(matcher.extractedField)
return message
}
func (matcher *HaveFieldMatcher) NegatedFailureMessage(actual interface{}) (message string) {
message = fmt.Sprintf("Value for field '%s' satisfied matcher, but should not have.\n", matcher.Field)
message += matcher.expectedMatcher.NegatedFailureMessage(matcher.extractedField)
return message
}

View File

@@ -0,0 +1,101 @@
package matchers
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/internal/gutil"
"github.com/onsi/gomega/types"
)
type HaveHTTPBodyMatcher struct {
Expected interface{}
cachedBody []byte
}
func (matcher *HaveHTTPBodyMatcher) Match(actual interface{}) (bool, error) {
body, err := matcher.body(actual)
if err != nil {
return false, err
}
switch e := matcher.Expected.(type) {
case string:
return (&EqualMatcher{Expected: e}).Match(string(body))
case []byte:
return (&EqualMatcher{Expected: e}).Match(body)
case types.GomegaMatcher:
return e.Match(body)
default:
return false, fmt.Errorf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1))
}
}
func (matcher *HaveHTTPBodyMatcher) FailureMessage(actual interface{}) (message string) {
body, err := matcher.body(actual)
if err != nil {
return fmt.Sprintf("failed to read body: %s", err)
}
switch e := matcher.Expected.(type) {
case string:
return (&EqualMatcher{Expected: e}).FailureMessage(string(body))
case []byte:
return (&EqualMatcher{Expected: e}).FailureMessage(body)
case types.GomegaMatcher:
return e.FailureMessage(body)
default:
return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1))
}
}
func (matcher *HaveHTTPBodyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
body, err := matcher.body(actual)
if err != nil {
return fmt.Sprintf("failed to read body: %s", err)
}
switch e := matcher.Expected.(type) {
case string:
return (&EqualMatcher{Expected: e}).NegatedFailureMessage(string(body))
case []byte:
return (&EqualMatcher{Expected: e}).NegatedFailureMessage(body)
case types.GomegaMatcher:
return e.NegatedFailureMessage(body)
default:
return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1))
}
}
// body returns the body. It is cached because once we read it in Match()
// the Reader is closed and it is not readable again in FailureMessage()
// or NegatedFailureMessage()
func (matcher *HaveHTTPBodyMatcher) body(actual interface{}) ([]byte, error) {
if matcher.cachedBody != nil {
return matcher.cachedBody, nil
}
body := func(a *http.Response) ([]byte, error) {
if a.Body != nil {
defer a.Body.Close()
var err error
matcher.cachedBody, err = gutil.ReadAll(a.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}
}
return matcher.cachedBody, nil
}
switch a := actual.(type) {
case *http.Response:
return body(a)
case *httptest.ResponseRecorder:
return body(a.Result())
default:
return nil, fmt.Errorf("HaveHTTPBody matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1))
}
}

View File

@@ -0,0 +1,81 @@
package matchers
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
type HaveHTTPHeaderWithValueMatcher struct {
Header string
Value interface{}
}
func (matcher *HaveHTTPHeaderWithValueMatcher) Match(actual interface{}) (success bool, err error) {
headerValue, err := matcher.extractHeader(actual)
if err != nil {
return false, err
}
headerMatcher, err := matcher.getSubMatcher()
if err != nil {
return false, err
}
return headerMatcher.Match(headerValue)
}
func (matcher *HaveHTTPHeaderWithValueMatcher) FailureMessage(actual interface{}) string {
headerValue, err := matcher.extractHeader(actual)
if err != nil {
panic(err) // protected by Match()
}
headerMatcher, err := matcher.getSubMatcher()
if err != nil {
panic(err) // protected by Match()
}
diff := format.IndentString(headerMatcher.FailureMessage(headerValue), 1)
return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff)
}
func (matcher *HaveHTTPHeaderWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) {
headerValue, err := matcher.extractHeader(actual)
if err != nil {
panic(err) // protected by Match()
}
headerMatcher, err := matcher.getSubMatcher()
if err != nil {
panic(err) // protected by Match()
}
diff := format.IndentString(headerMatcher.NegatedFailureMessage(headerValue), 1)
return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff)
}
func (matcher *HaveHTTPHeaderWithValueMatcher) getSubMatcher() (types.GomegaMatcher, error) {
switch m := matcher.Value.(type) {
case string:
return &EqualMatcher{Expected: matcher.Value}, nil
case types.GomegaMatcher:
return m, nil
default:
return nil, fmt.Errorf("HaveHTTPHeaderWithValue matcher must be passed a string or a GomegaMatcher. Got:\n%s", format.Object(matcher.Value, 1))
}
}
func (matcher *HaveHTTPHeaderWithValueMatcher) extractHeader(actual interface{}) (string, error) {
switch r := actual.(type) {
case *http.Response:
return r.Header.Get(matcher.Header), nil
case *httptest.ResponseRecorder:
return r.Result().Header.Get(matcher.Header), nil
default:
return "", fmt.Errorf("HaveHTTPHeaderWithValue matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1))
}
}

View File

@@ -0,0 +1,96 @@
package matchers
import (
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/internal/gutil"
)
type HaveHTTPStatusMatcher struct {
Expected []interface{}
}
func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, err error) {
var resp *http.Response
switch a := actual.(type) {
case *http.Response:
resp = a
case *httptest.ResponseRecorder:
resp = a.Result()
default:
return false, fmt.Errorf("HaveHTTPStatus matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1))
}
if len(matcher.Expected) == 0 {
return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got nothing")
}
for _, expected := range matcher.Expected {
switch e := expected.(type) {
case int:
if resp.StatusCode == e {
return true, nil
}
case string:
if resp.Status == e {
return true, nil
}
default:
return false, fmt.Errorf("HaveHTTPStatus matcher must be passed int or string types. Got:\n%s", format.Object(expected, 1))
}
}
return false, nil
}
func (matcher *HaveHTTPStatusMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n%s\n%s\n%s", formatHttpResponse(actual), "to have HTTP status", matcher.expectedString())
}
func (matcher *HaveHTTPStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n%s\n%s\n%s", formatHttpResponse(actual), "not to have HTTP status", matcher.expectedString())
}
func (matcher *HaveHTTPStatusMatcher) expectedString() string {
var lines []string
for _, expected := range matcher.Expected {
lines = append(lines, format.Object(expected, 1))
}
return strings.Join(lines, "\n")
}
func formatHttpResponse(input interface{}) string {
var resp *http.Response
switch r := input.(type) {
case *http.Response:
resp = r
case *httptest.ResponseRecorder:
resp = r.Result()
default:
return "cannot format invalid HTTP response"
}
body := "<nil>"
if resp.Body != nil {
defer resp.Body.Close()
data, err := gutil.ReadAll(resp.Body)
if err != nil {
data = []byte("<error reading body>")
}
body = format.Object(string(data), 0)
}
var s strings.Builder
s.WriteString(fmt.Sprintf("%s<%s>: {\n", format.Indent, reflect.TypeOf(input)))
s.WriteString(fmt.Sprintf("%s%sStatus: %s\n", format.Indent, format.Indent, format.Object(resp.Status, 0)))
s.WriteString(fmt.Sprintf("%s%sStatusCode: %s\n", format.Indent, format.Indent, format.Object(resp.StatusCode, 0)))
s.WriteString(fmt.Sprintf("%s%sBody: %s\n", format.Indent, format.Indent, body))
s.WriteString(fmt.Sprintf("%s}", format.Indent))
return s.String()
}

View File

@@ -0,0 +1,56 @@
// untested sections: 6
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type HaveKeyMatcher struct {
Key interface{}
}
func (matcher *HaveKeyMatcher) Match(actual interface{}) (success bool, err error) {
if !isMap(actual) {
return false, fmt.Errorf("HaveKey matcher expects a map. Got:%s", format.Object(actual, 1))
}
keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher)
if !keyIsMatcher {
keyMatcher = &EqualMatcher{Expected: matcher.Key}
}
keys := reflect.ValueOf(actual).MapKeys()
for i := 0; i < len(keys); i++ {
success, err := keyMatcher.Match(keys[i].Interface())
if err != nil {
return false, fmt.Errorf("HaveKey's key matcher failed with:\n%s%s", format.Indent, err.Error())
}
if success {
return true, nil
}
}
return false, nil
}
func (matcher *HaveKeyMatcher) FailureMessage(actual interface{}) (message string) {
switch matcher.Key.(type) {
case omegaMatcher:
return format.Message(actual, "to have key matching", matcher.Key)
default:
return format.Message(actual, "to have key", matcher.Key)
}
}
func (matcher *HaveKeyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
switch matcher.Key.(type) {
case omegaMatcher:
return format.Message(actual, "not to have key matching", matcher.Key)
default:
return format.Message(actual, "not to have key", matcher.Key)
}
}

View File

@@ -0,0 +1,76 @@
// untested sections:10
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type HaveKeyWithValueMatcher struct {
Key interface{}
Value interface{}
}
func (matcher *HaveKeyWithValueMatcher) Match(actual interface{}) (success bool, err error) {
if !isMap(actual) {
return false, fmt.Errorf("HaveKeyWithValue matcher expects a map. Got:%s", format.Object(actual, 1))
}
keyMatcher, keyIsMatcher := matcher.Key.(omegaMatcher)
if !keyIsMatcher {
keyMatcher = &EqualMatcher{Expected: matcher.Key}
}
valueMatcher, valueIsMatcher := matcher.Value.(omegaMatcher)
if !valueIsMatcher {
valueMatcher = &EqualMatcher{Expected: matcher.Value}
}
keys := reflect.ValueOf(actual).MapKeys()
for i := 0; i < len(keys); i++ {
success, err := keyMatcher.Match(keys[i].Interface())
if err != nil {
return false, fmt.Errorf("HaveKeyWithValue's key matcher failed with:\n%s%s", format.Indent, err.Error())
}
if success {
actualValue := reflect.ValueOf(actual).MapIndex(keys[i])
success, err := valueMatcher.Match(actualValue.Interface())
if err != nil {
return false, fmt.Errorf("HaveKeyWithValue's value matcher failed with:\n%s%s", format.Indent, err.Error())
}
return success, nil
}
}
return false, nil
}
func (matcher *HaveKeyWithValueMatcher) FailureMessage(actual interface{}) (message string) {
str := "to have {key: value}"
if _, ok := matcher.Key.(omegaMatcher); ok {
str += " matching"
} else if _, ok := matcher.Value.(omegaMatcher); ok {
str += " matching"
}
expect := make(map[interface{}]interface{}, 1)
expect[matcher.Key] = matcher.Value
return format.Message(actual, str, expect)
}
func (matcher *HaveKeyWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) {
kStr := "not to have key"
if _, ok := matcher.Key.(omegaMatcher); ok {
kStr = "not to have key matching"
}
vStr := "or that key's value not be"
if _, ok := matcher.Value.(omegaMatcher); ok {
vStr = "or to have that key's value not matching"
}
return format.Message(actual, kStr, matcher.Key, vStr, matcher.Value)
}

View File

@@ -0,0 +1,28 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type HaveLenMatcher struct {
Count int
}
func (matcher *HaveLenMatcher) Match(actual interface{}) (success bool, err error) {
length, ok := lengthOf(actual)
if !ok {
return false, fmt.Errorf("HaveLen matcher expects a string/array/map/channel/slice. Got:\n%s", format.Object(actual, 1))
}
return length == matcher.Count, nil
}
func (matcher *HaveLenMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n%s\nto have length %d", format.Object(actual, 1), matcher.Count)
}
func (matcher *HaveLenMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected\n%s\nnot to have length %d", format.Object(actual, 1), matcher.Count)
}

View File

@@ -0,0 +1,35 @@
// untested sections: 2
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type HaveOccurredMatcher struct {
}
func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) {
// is purely nil?
if actual == nil {
return false, nil
}
// must be an 'error' type
if !isError(actual) {
return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1))
}
// must be non-nil (or a pointer to a non-nil)
return !isNil(actual), nil
}
func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected an error to have occurred. Got:\n%s", format.Object(actual, 1))
}
func (matcher *HaveOccurredMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Unexpected error:\n%s\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1), "occurred")
}

View File

@@ -0,0 +1,36 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type HavePrefixMatcher struct {
Prefix string
Args []interface{}
}
func (matcher *HavePrefixMatcher) Match(actual interface{}) (success bool, err error) {
actualString, ok := toString(actual)
if !ok {
return false, fmt.Errorf("HavePrefix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1))
}
prefix := matcher.prefix()
return len(actualString) >= len(prefix) && actualString[0:len(prefix)] == prefix, nil
}
func (matcher *HavePrefixMatcher) prefix() string {
if len(matcher.Args) > 0 {
return fmt.Sprintf(matcher.Prefix, matcher.Args...)
}
return matcher.Prefix
}
func (matcher *HavePrefixMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to have prefix", matcher.prefix())
}
func (matcher *HavePrefixMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to have prefix", matcher.prefix())
}

View File

@@ -0,0 +1,36 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type HaveSuffixMatcher struct {
Suffix string
Args []interface{}
}
func (matcher *HaveSuffixMatcher) Match(actual interface{}) (success bool, err error) {
actualString, ok := toString(actual)
if !ok {
return false, fmt.Errorf("HaveSuffix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1))
}
suffix := matcher.suffix()
return len(actualString) >= len(suffix) && actualString[len(actualString)-len(suffix):] == suffix, nil
}
func (matcher *HaveSuffixMatcher) suffix() string {
if len(matcher.Args) > 0 {
return fmt.Sprintf(matcher.Suffix, matcher.Args...)
}
return matcher.Suffix
}
func (matcher *HaveSuffixMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to have suffix", matcher.suffix())
}
func (matcher *HaveSuffixMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to have suffix", matcher.suffix())
}

54
vendor/github.com/onsi/gomega/matchers/have_value.go generated vendored Normal file
View File

@@ -0,0 +1,54 @@
package matchers
import (
"errors"
"reflect"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
const maxIndirections = 31
type HaveValueMatcher struct {
Matcher types.GomegaMatcher // the matcher to apply to the "resolved" actual value.
resolvedActual interface{} // the ("resolved") value.
}
func (m *HaveValueMatcher) Match(actual interface{}) (bool, error) {
val := reflect.ValueOf(actual)
for allowedIndirs := maxIndirections; allowedIndirs > 0; allowedIndirs-- {
// return an error if value isn't valid. Please note that we cannot
// check for nil here, as we might not deal with a pointer or interface
// at this point.
if !val.IsValid() {
return false, errors.New(format.Message(
actual, "not to be <nil>"))
}
switch val.Kind() {
case reflect.Ptr, reflect.Interface:
// resolve pointers and interfaces to their values, then rinse and
// repeat.
if val.IsNil() {
return false, errors.New(format.Message(
actual, "not to be <nil>"))
}
val = val.Elem()
continue
default:
// forward the final value to the specified matcher.
m.resolvedActual = val.Interface()
return m.Matcher.Match(m.resolvedActual)
}
}
// too many indirections: extreme star gazing, indeed...?
return false, errors.New(format.Message(actual, "too many indirections"))
}
func (m *HaveValueMatcher) FailureMessage(_ interface{}) (message string) {
return m.Matcher.FailureMessage(m.resolvedActual)
}
func (m *HaveValueMatcher) NegatedFailureMessage(_ interface{}) (message string) {
return m.Matcher.NegatedFailureMessage(m.resolvedActual)
}

View File

@@ -0,0 +1,55 @@
package matchers
import (
"errors"
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type MatchErrorMatcher struct {
Expected interface{}
}
func (matcher *MatchErrorMatcher) Match(actual interface{}) (success bool, err error) {
if isNil(actual) {
return false, fmt.Errorf("Expected an error, got nil")
}
if !isError(actual) {
return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1))
}
actualErr := actual.(error)
expected := matcher.Expected
if isError(expected) {
return reflect.DeepEqual(actualErr, expected) || errors.Is(actualErr, expected.(error)), nil
}
if isString(expected) {
return actualErr.Error() == expected, nil
}
var subMatcher omegaMatcher
var hasSubMatcher bool
if expected != nil {
subMatcher, hasSubMatcher = (expected).(omegaMatcher)
if hasSubMatcher {
return subMatcher.Match(actualErr.Error())
}
}
return false, fmt.Errorf(
"MatchError must be passed an error, a string, or a Matcher that can match on strings. Got:\n%s",
format.Object(expected, 1))
}
func (matcher *MatchErrorMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to match error", matcher.Expected)
}
func (matcher *MatchErrorMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to match error", matcher.Expected)
}

View File

@@ -0,0 +1,65 @@
package matchers
import (
"bytes"
"encoding/json"
"fmt"
"github.com/onsi/gomega/format"
)
type MatchJSONMatcher struct {
JSONToMatch interface{}
firstFailurePath []interface{}
}
func (matcher *MatchJSONMatcher) Match(actual interface{}) (success bool, err error) {
actualString, expectedString, err := matcher.prettyPrint(actual)
if err != nil {
return false, err
}
var aval interface{}
var eval interface{}
// this is guarded by prettyPrint
json.Unmarshal([]byte(actualString), &aval)
json.Unmarshal([]byte(expectedString), &eval)
var equal bool
equal, matcher.firstFailurePath = deepEqual(aval, eval)
return equal, nil
}
func (matcher *MatchJSONMatcher) FailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.prettyPrint(actual)
return formattedMessage(format.Message(actualString, "to match JSON of", expectedString), matcher.firstFailurePath)
}
func (matcher *MatchJSONMatcher) NegatedFailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.prettyPrint(actual)
return formattedMessage(format.Message(actualString, "not to match JSON of", expectedString), matcher.firstFailurePath)
}
func (matcher *MatchJSONMatcher) prettyPrint(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
actualString, ok := toString(actual)
if !ok {
return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
}
expectedString, ok := toString(matcher.JSONToMatch)
if !ok {
return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.JSONToMatch, 1))
}
abuf := new(bytes.Buffer)
ebuf := new(bytes.Buffer)
if err := json.Indent(abuf, []byte(actualString), "", " "); err != nil {
return "", "", fmt.Errorf("Actual '%s' should be valid JSON, but it is not.\nUnderlying error:%s", actualString, err)
}
if err := json.Indent(ebuf, []byte(expectedString), "", " "); err != nil {
return "", "", fmt.Errorf("Expected '%s' should be valid JSON, but it is not.\nUnderlying error:%s", expectedString, err)
}
return abuf.String(), ebuf.String(), nil
}

View File

@@ -0,0 +1,43 @@
package matchers
import (
"fmt"
"regexp"
"github.com/onsi/gomega/format"
)
type MatchRegexpMatcher struct {
Regexp string
Args []interface{}
}
func (matcher *MatchRegexpMatcher) Match(actual interface{}) (success bool, err error) {
actualString, ok := toString(actual)
if !ok {
return false, fmt.Errorf("RegExp matcher requires a string or stringer.\nGot:%s", format.Object(actual, 1))
}
match, err := regexp.Match(matcher.regexp(), []byte(actualString))
if err != nil {
return false, fmt.Errorf("RegExp match failed to compile with error:\n\t%s", err.Error())
}
return match, nil
}
func (matcher *MatchRegexpMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to match regular expression", matcher.regexp())
}
func (matcher *MatchRegexpMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to match regular expression", matcher.regexp())
}
func (matcher *MatchRegexpMatcher) regexp() string {
re := matcher.Regexp
if len(matcher.Args) > 0 {
re = fmt.Sprintf(matcher.Regexp, matcher.Args...)
}
return re
}

View File

@@ -0,0 +1,134 @@
package matchers
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"reflect"
"sort"
"strings"
"github.com/onsi/gomega/format"
"golang.org/x/net/html/charset"
)
type MatchXMLMatcher struct {
XMLToMatch interface{}
}
func (matcher *MatchXMLMatcher) Match(actual interface{}) (success bool, err error) {
actualString, expectedString, err := matcher.formattedPrint(actual)
if err != nil {
return false, err
}
aval, err := parseXmlContent(actualString)
if err != nil {
return false, fmt.Errorf("Actual '%s' should be valid XML, but it is not.\nUnderlying error:%s", actualString, err)
}
eval, err := parseXmlContent(expectedString)
if err != nil {
return false, fmt.Errorf("Expected '%s' should be valid XML, but it is not.\nUnderlying error:%s", expectedString, err)
}
return reflect.DeepEqual(aval, eval), nil
}
func (matcher *MatchXMLMatcher) FailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.formattedPrint(actual)
return fmt.Sprintf("Expected\n%s\nto match XML of\n%s", actualString, expectedString)
}
func (matcher *MatchXMLMatcher) NegatedFailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.formattedPrint(actual)
return fmt.Sprintf("Expected\n%s\nnot to match XML of\n%s", actualString, expectedString)
}
func (matcher *MatchXMLMatcher) formattedPrint(actual interface{}) (actualString, expectedString string, err error) {
var ok bool
actualString, ok = toString(actual)
if !ok {
return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
}
expectedString, ok = toString(matcher.XMLToMatch)
if !ok {
return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.XMLToMatch, 1))
}
return actualString, expectedString, nil
}
func parseXmlContent(content string) (*xmlNode, error) {
allNodes := []*xmlNode{}
dec := newXmlDecoder(strings.NewReader(content))
for {
tok, err := dec.Token()
if err != nil {
if err == io.EOF {
break
}
return nil, fmt.Errorf("failed to decode next token: %v", err) // untested section
}
lastNodeIndex := len(allNodes) - 1
var lastNode *xmlNode
if len(allNodes) > 0 {
lastNode = allNodes[lastNodeIndex]
} else {
lastNode = &xmlNode{}
}
switch tok := tok.(type) {
case xml.StartElement:
attrs := attributesSlice(tok.Attr)
sort.Sort(attrs)
allNodes = append(allNodes, &xmlNode{XMLName: tok.Name, XMLAttr: tok.Attr})
case xml.EndElement:
if len(allNodes) > 1 {
allNodes[lastNodeIndex-1].Nodes = append(allNodes[lastNodeIndex-1].Nodes, lastNode)
allNodes = allNodes[:lastNodeIndex]
}
case xml.CharData:
lastNode.Content = append(lastNode.Content, tok.Copy()...)
case xml.Comment:
lastNode.Comments = append(lastNode.Comments, tok.Copy()) // untested section
case xml.ProcInst:
lastNode.ProcInsts = append(lastNode.ProcInsts, tok.Copy())
}
}
if len(allNodes) == 0 {
return nil, errors.New("found no nodes")
}
firstNode := allNodes[0]
trimParentNodesContentSpaces(firstNode)
return firstNode, nil
}
func newXmlDecoder(reader io.Reader) *xml.Decoder {
dec := xml.NewDecoder(reader)
dec.CharsetReader = charset.NewReaderLabel
return dec
}
func trimParentNodesContentSpaces(node *xmlNode) {
if len(node.Nodes) > 0 {
node.Content = bytes.TrimSpace(node.Content)
for _, childNode := range node.Nodes {
trimParentNodesContentSpaces(childNode)
}
}
}
type xmlNode struct {
XMLName xml.Name
Comments []xml.Comment
ProcInsts []xml.ProcInst
XMLAttr []xml.Attr
Content []byte
Nodes []*xmlNode
}

View File

@@ -0,0 +1,76 @@
package matchers
import (
"fmt"
"strings"
"github.com/onsi/gomega/format"
"gopkg.in/yaml.v2"
)
type MatchYAMLMatcher struct {
YAMLToMatch interface{}
firstFailurePath []interface{}
}
func (matcher *MatchYAMLMatcher) Match(actual interface{}) (success bool, err error) {
actualString, expectedString, err := matcher.toStrings(actual)
if err != nil {
return false, err
}
var aval interface{}
var eval interface{}
if err := yaml.Unmarshal([]byte(actualString), &aval); err != nil {
return false, fmt.Errorf("Actual '%s' should be valid YAML, but it is not.\nUnderlying error:%s", actualString, err)
}
if err := yaml.Unmarshal([]byte(expectedString), &eval); err != nil {
return false, fmt.Errorf("Expected '%s' should be valid YAML, but it is not.\nUnderlying error:%s", expectedString, err)
}
var equal bool
equal, matcher.firstFailurePath = deepEqual(aval, eval)
return equal, nil
}
func (matcher *MatchYAMLMatcher) FailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.toNormalisedStrings(actual)
return formattedMessage(format.Message(actualString, "to match YAML of", expectedString), matcher.firstFailurePath)
}
func (matcher *MatchYAMLMatcher) NegatedFailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.toNormalisedStrings(actual)
return formattedMessage(format.Message(actualString, "not to match YAML of", expectedString), matcher.firstFailurePath)
}
func (matcher *MatchYAMLMatcher) toNormalisedStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
actualString, expectedString, err := matcher.toStrings(actual)
return normalise(actualString), normalise(expectedString), err
}
func normalise(input string) string {
var val interface{}
err := yaml.Unmarshal([]byte(input), &val)
if err != nil {
panic(err) // unreachable since Match already calls Unmarshal
}
output, err := yaml.Marshal(val)
if err != nil {
panic(err) // untested section, unreachable since we Unmarshal above
}
return strings.TrimSpace(string(output))
}
func (matcher *MatchYAMLMatcher) toStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
actualString, ok := toString(actual)
if !ok {
return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
}
expectedString, ok := toString(matcher.YAMLToMatch)
if !ok {
return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.YAMLToMatch, 1))
}
return actualString, expectedString, nil
}

29
vendor/github.com/onsi/gomega/matchers/not.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package matchers
import (
"github.com/onsi/gomega/types"
)
type NotMatcher struct {
Matcher types.GomegaMatcher
}
func (m *NotMatcher) Match(actual interface{}) (bool, error) {
success, err := m.Matcher.Match(actual)
if err != nil {
return false, err
}
return !success, nil
}
func (m *NotMatcher) FailureMessage(actual interface{}) (message string) {
return m.Matcher.NegatedFailureMessage(actual) // works beautifully
}
func (m *NotMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return m.Matcher.FailureMessage(actual) // works beautifully
}
func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
return types.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value
}

66
vendor/github.com/onsi/gomega/matchers/or.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
type OrMatcher struct {
Matchers []types.GomegaMatcher
// state
firstSuccessfulMatcher types.GomegaMatcher
}
func (m *OrMatcher) Match(actual interface{}) (success bool, err error) {
m.firstSuccessfulMatcher = nil
for _, matcher := range m.Matchers {
success, err := matcher.Match(actual)
if err != nil {
return false, err
}
if success {
m.firstSuccessfulMatcher = matcher
return true, nil
}
}
return false, nil
}
func (m *OrMatcher) FailureMessage(actual interface{}) (message string) {
// not the most beautiful list of matchers, but not bad either...
return format.Message(actual, fmt.Sprintf("To satisfy at least one of these matchers: %s", m.Matchers))
}
func (m *OrMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return m.firstSuccessfulMatcher.NegatedFailureMessage(actual)
}
func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
/*
Example with 3 matchers: A, B, C
Match evaluates them: F, T, <?> => T
So match is currently T, what should MatchMayChangeInTheFuture() return?
Seems like it only depends on B, since currently B MUST change to allow the result to become F
Match eval: F, F, F => F
So match is currently F, what should MatchMayChangeInTheFuture() return?
Seems to depend on ANY of them being able to change to T.
*/
if m.firstSuccessfulMatcher != nil {
// one of the matchers succeeded.. it must be able to change in order to affect the result
return types.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
} else {
// so all matchers failed.. Any one of them changing would change the result.
for _, matcher := range m.Matchers {
if types.MatchMayChangeInTheFuture(matcher, actual) {
return true
}
}
return false // none of were going to change
}
}

114
vendor/github.com/onsi/gomega/matchers/panic_matcher.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type PanicMatcher struct {
Expected interface{}
object interface{}
}
func (matcher *PanicMatcher) Match(actual interface{}) (success bool, err error) {
if actual == nil {
return false, fmt.Errorf("PanicMatcher expects a non-nil actual.")
}
actualType := reflect.TypeOf(actual)
if actualType.Kind() != reflect.Func {
return false, fmt.Errorf("PanicMatcher expects a function. Got:\n%s", format.Object(actual, 1))
}
if !(actualType.NumIn() == 0 && actualType.NumOut() == 0) {
return false, fmt.Errorf("PanicMatcher expects a function with no arguments and no return value. Got:\n%s", format.Object(actual, 1))
}
success = false
defer func() {
if e := recover(); e != nil {
matcher.object = e
if matcher.Expected == nil {
success = true
return
}
valueMatcher, valueIsMatcher := matcher.Expected.(omegaMatcher)
if !valueIsMatcher {
valueMatcher = &EqualMatcher{Expected: matcher.Expected}
}
success, err = valueMatcher.Match(e)
if err != nil {
err = fmt.Errorf("PanicMatcher's value matcher failed with:\n%s%s", format.Indent, err.Error())
}
}
}()
reflect.ValueOf(actual).Call([]reflect.Value{})
return
}
func (matcher *PanicMatcher) FailureMessage(actual interface{}) (message string) {
if matcher.Expected == nil {
// We wanted any panic to occur, but none did.
return format.Message(actual, "to panic")
}
if matcher.object == nil {
// We wanted a panic with a specific value to occur, but none did.
switch matcher.Expected.(type) {
case omegaMatcher:
return format.Message(actual, "to panic with a value matching", matcher.Expected)
default:
return format.Message(actual, "to panic with", matcher.Expected)
}
}
// We got a panic, but the value isn't what we expected.
switch matcher.Expected.(type) {
case omegaMatcher:
return format.Message(
actual,
fmt.Sprintf(
"to panic with a value matching\n%s\nbut panicked with\n%s",
format.Object(matcher.Expected, 1),
format.Object(matcher.object, 1),
),
)
default:
return format.Message(
actual,
fmt.Sprintf(
"to panic with\n%s\nbut panicked with\n%s",
format.Object(matcher.Expected, 1),
format.Object(matcher.object, 1),
),
)
}
}
func (matcher *PanicMatcher) NegatedFailureMessage(actual interface{}) (message string) {
if matcher.Expected == nil {
// We didn't want any panic to occur, but one did.
return format.Message(actual, fmt.Sprintf("not to panic, but panicked with\n%s", format.Object(matcher.object, 1)))
}
// We wanted a to ensure a panic with a specific value did not occur, but it did.
switch matcher.Expected.(type) {
case omegaMatcher:
return format.Message(
actual,
fmt.Sprintf(
"not to panic with a value matching\n%s\nbut panicked with\n%s",
format.Object(matcher.Expected, 1),
format.Object(matcher.object, 1),
),
)
default:
return format.Message(actual, "not to panic with", matcher.Expected)
}
}

View File

@@ -0,0 +1,130 @@
// untested sections: 3
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type ReceiveMatcher struct {
Arg interface{}
receivedValue reflect.Value
channelClosed bool
}
func (matcher *ReceiveMatcher) Match(actual interface{}) (success bool, err error) {
if !isChan(actual) {
return false, fmt.Errorf("ReceiveMatcher expects a channel. Got:\n%s", format.Object(actual, 1))
}
channelType := reflect.TypeOf(actual)
channelValue := reflect.ValueOf(actual)
if channelType.ChanDir() == reflect.SendDir {
return false, fmt.Errorf("ReceiveMatcher matcher cannot be passed a send-only channel. Got:\n%s", format.Object(actual, 1))
}
var subMatcher omegaMatcher
var hasSubMatcher bool
if matcher.Arg != nil {
subMatcher, hasSubMatcher = (matcher.Arg).(omegaMatcher)
if !hasSubMatcher {
argType := reflect.TypeOf(matcher.Arg)
if argType.Kind() != reflect.Ptr {
return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s\nYou need to pass a pointer!", format.Object(actual, 1), format.Object(matcher.Arg, 1))
}
}
}
winnerIndex, value, open := reflect.Select([]reflect.SelectCase{
{Dir: reflect.SelectRecv, Chan: channelValue},
{Dir: reflect.SelectDefault},
})
var closed bool
var didReceive bool
if winnerIndex == 0 {
closed = !open
didReceive = open
}
matcher.channelClosed = closed
if closed {
return false, nil
}
if hasSubMatcher {
if didReceive {
matcher.receivedValue = value
return subMatcher.Match(matcher.receivedValue.Interface())
}
return false, nil
}
if didReceive {
if matcher.Arg != nil {
outValue := reflect.ValueOf(matcher.Arg)
if value.Type().AssignableTo(outValue.Elem().Type()) {
outValue.Elem().Set(value)
return true, nil
}
if value.Type().Kind() == reflect.Interface && value.Elem().Type().AssignableTo(outValue.Elem().Type()) {
outValue.Elem().Set(value.Elem())
return true, nil
} else {
return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nType:\n%s\nTo:\n%s", format.Object(actual, 1), format.Object(value.Interface(), 1), format.Object(matcher.Arg, 1))
}
}
return true, nil
}
return false, nil
}
func (matcher *ReceiveMatcher) FailureMessage(actual interface{}) (message string) {
subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher)
closedAddendum := ""
if matcher.channelClosed {
closedAddendum = " The channel is closed."
}
if hasSubMatcher {
if matcher.receivedValue.IsValid() {
return subMatcher.FailureMessage(matcher.receivedValue.Interface())
}
return "When passed a matcher, ReceiveMatcher's channel *must* receive something."
}
return format.Message(actual, "to receive something."+closedAddendum)
}
func (matcher *ReceiveMatcher) NegatedFailureMessage(actual interface{}) (message string) {
subMatcher, hasSubMatcher := (matcher.Arg).(omegaMatcher)
closedAddendum := ""
if matcher.channelClosed {
closedAddendum = " The channel is closed."
}
if hasSubMatcher {
if matcher.receivedValue.IsValid() {
return subMatcher.NegatedFailureMessage(matcher.receivedValue.Interface())
}
return "When passed a matcher, ReceiveMatcher's channel *must* receive something."
}
return format.Message(actual, "not to receive anything."+closedAddendum)
}
func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
if !isChan(actual) {
return false
}
return !matcher.channelClosed
}

View File

@@ -0,0 +1,66 @@
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
)
type SatisfyMatcher struct {
Predicate interface{}
// cached type
predicateArgType reflect.Type
}
func NewSatisfyMatcher(predicate interface{}) *SatisfyMatcher {
if predicate == nil {
panic("predicate cannot be nil")
}
predicateType := reflect.TypeOf(predicate)
if predicateType.Kind() != reflect.Func {
panic("predicate must be a function")
}
if predicateType.NumIn() != 1 {
panic("predicate must have 1 argument")
}
if predicateType.NumOut() != 1 || predicateType.Out(0).Kind() != reflect.Bool {
panic("predicate must return bool")
}
return &SatisfyMatcher{
Predicate: predicate,
predicateArgType: predicateType.In(0),
}
}
func (m *SatisfyMatcher) Match(actual interface{}) (success bool, err error) {
// prepare a parameter to pass to the predicate
var param reflect.Value
if actual != nil && reflect.TypeOf(actual).AssignableTo(m.predicateArgType) {
// The dynamic type of actual is compatible with the predicate argument.
param = reflect.ValueOf(actual)
} else if actual == nil && m.predicateArgType.Kind() == reflect.Interface {
// The dynamic type of actual is unknown, so there's no way to make its
// reflect.Value. Create a nil of the predicate argument, which is known.
param = reflect.Zero(m.predicateArgType)
} else {
return false, fmt.Errorf("predicate expects '%s' but we have '%T'", m.predicateArgType, actual)
}
// call the predicate with `actual`
fn := reflect.ValueOf(m.Predicate)
result := fn.Call([]reflect.Value{param})
return result[0].Bool(), nil
}
func (m *SatisfyMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to satisfy predicate", m.Predicate)
}
func (m *SatisfyMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to not satisfy predicate", m.Predicate)
}

View File

@@ -0,0 +1,94 @@
// untested sections: 5
package matchers
import (
"fmt"
"reflect"
"strings"
)
func formattedMessage(comparisonMessage string, failurePath []interface{}) string {
var diffMessage string
if len(failurePath) == 0 {
diffMessage = ""
} else {
diffMessage = fmt.Sprintf("\n\nfirst mismatched key: %s", formattedFailurePath(failurePath))
}
return fmt.Sprintf("%s%s", comparisonMessage, diffMessage)
}
func formattedFailurePath(failurePath []interface{}) string {
formattedPaths := []string{}
for i := len(failurePath) - 1; i >= 0; i-- {
switch p := failurePath[i].(type) {
case int:
formattedPaths = append(formattedPaths, fmt.Sprintf(`[%d]`, p))
default:
if i != len(failurePath)-1 {
formattedPaths = append(formattedPaths, ".")
}
formattedPaths = append(formattedPaths, fmt.Sprintf(`"%s"`, p))
}
}
return strings.Join(formattedPaths, "")
}
func deepEqual(a interface{}, b interface{}) (bool, []interface{}) {
var errorPath []interface{}
if reflect.TypeOf(a) != reflect.TypeOf(b) {
return false, errorPath
}
switch a.(type) {
case []interface{}:
if len(a.([]interface{})) != len(b.([]interface{})) {
return false, errorPath
}
for i, v := range a.([]interface{}) {
elementEqual, keyPath := deepEqual(v, b.([]interface{})[i])
if !elementEqual {
return false, append(keyPath, i)
}
}
return true, errorPath
case map[interface{}]interface{}:
if len(a.(map[interface{}]interface{})) != len(b.(map[interface{}]interface{})) {
return false, errorPath
}
for k, v1 := range a.(map[interface{}]interface{}) {
v2, ok := b.(map[interface{}]interface{})[k]
if !ok {
return false, errorPath
}
elementEqual, keyPath := deepEqual(v1, v2)
if !elementEqual {
return false, append(keyPath, k)
}
}
return true, errorPath
case map[string]interface{}:
if len(a.(map[string]interface{})) != len(b.(map[string]interface{})) {
return false, errorPath
}
for k, v1 := range a.(map[string]interface{}) {
v2, ok := b.(map[string]interface{})[k]
if !ok {
return false, errorPath
}
elementEqual, keyPath := deepEqual(v1, v2)
if !elementEqual {
return false, append(keyPath, k)
}
}
return true, errorPath
default:
return a == b, errorPath
}
}

View File

@@ -0,0 +1,33 @@
package matchers
import (
"fmt"
"github.com/onsi/gomega/format"
)
type SucceedMatcher struct {
}
func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) {
// is purely nil?
if actual == nil {
return true, nil
}
// must be an 'error' type
if !isError(actual) {
return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1))
}
// must be nil (or a pointer to a nil)
return isNil(actual), nil
}
func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Expected success, but got an error:\n%s\n%s", format.Object(actual, 1), format.IndentString(actual.(error).Error(), 1))
}
func (matcher *SucceedMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return "Expected failure, but got no error."
}

View File

@@ -0,0 +1,56 @@
package bipartitegraph
import "fmt"
import . "github.com/onsi/gomega/matchers/support/goraph/node"
import . "github.com/onsi/gomega/matchers/support/goraph/edge"
type BipartiteGraph struct {
Left NodeOrderedSet
Right NodeOrderedSet
Edges EdgeSet
}
func NewBipartiteGraph(leftValues, rightValues []interface{}, neighbours func(interface{}, interface{}) (bool, error)) (*BipartiteGraph, error) {
left := NodeOrderedSet{}
for i, v := range leftValues {
left = append(left, Node{ID: i, Value: v})
}
right := NodeOrderedSet{}
for j, v := range rightValues {
right = append(right, Node{ID: j + len(left), Value: v})
}
edges := EdgeSet{}
for i, leftValue := range leftValues {
for j, rightValue := range rightValues {
neighbours, err := neighbours(leftValue, rightValue)
if err != nil {
return nil, fmt.Errorf("error determining adjacency for %v and %v: %s", leftValue, rightValue, err.Error())
}
if neighbours {
edges = append(edges, Edge{Node1: left[i].ID, Node2: right[j].ID})
}
}
}
return &BipartiteGraph{left, right, edges}, nil
}
// FreeLeftRight returns left node values and right node values
// of the BipartiteGraph's nodes which are not part of the given edges.
func (bg *BipartiteGraph) FreeLeftRight(edges EdgeSet) (leftValues, rightValues []interface{}) {
for _, node := range bg.Left {
if edges.Free(node) {
leftValues = append(leftValues, node.Value)
}
}
for _, node := range bg.Right {
if edges.Free(node) {
rightValues = append(rightValues, node.Value)
}
}
return
}

View File

@@ -0,0 +1,164 @@
package bipartitegraph
import (
. "github.com/onsi/gomega/matchers/support/goraph/edge"
. "github.com/onsi/gomega/matchers/support/goraph/node"
"github.com/onsi/gomega/matchers/support/goraph/util"
)
// LargestMatching implements the HopcroftKarp algorithm taking as input a bipartite graph
// and outputting a maximum cardinality matching, i.e. a set of as many edges as possible
// with the property that no two edges share an endpoint.
func (bg *BipartiteGraph) LargestMatching() (matching EdgeSet) {
paths := bg.maximalDisjointSLAPCollection(matching)
for len(paths) > 0 {
for _, path := range paths {
matching = matching.SymmetricDifference(path)
}
paths = bg.maximalDisjointSLAPCollection(matching)
}
return
}
func (bg *BipartiteGraph) maximalDisjointSLAPCollection(matching EdgeSet) (result []EdgeSet) {
guideLayers := bg.createSLAPGuideLayers(matching)
if len(guideLayers) == 0 {
return
}
used := make(map[int]bool)
for _, u := range guideLayers[len(guideLayers)-1] {
slap, found := bg.findDisjointSLAP(u, matching, guideLayers, used)
if found {
for _, edge := range slap {
used[edge.Node1] = true
used[edge.Node2] = true
}
result = append(result, slap)
}
}
return
}
func (bg *BipartiteGraph) findDisjointSLAP(
start Node,
matching EdgeSet,
guideLayers []NodeOrderedSet,
used map[int]bool,
) ([]Edge, bool) {
return bg.findDisjointSLAPHelper(start, EdgeSet{}, len(guideLayers)-1, matching, guideLayers, used)
}
func (bg *BipartiteGraph) findDisjointSLAPHelper(
currentNode Node,
currentSLAP EdgeSet,
currentLevel int,
matching EdgeSet,
guideLayers []NodeOrderedSet,
used map[int]bool,
) (EdgeSet, bool) {
used[currentNode.ID] = true
if currentLevel == 0 {
return currentSLAP, true
}
for _, nextNode := range guideLayers[currentLevel-1] {
if used[nextNode.ID] {
continue
}
edge, found := bg.Edges.FindByNodes(currentNode, nextNode)
if !found {
continue
}
if matching.Contains(edge) == util.Odd(currentLevel) {
continue
}
currentSLAP = append(currentSLAP, edge)
slap, found := bg.findDisjointSLAPHelper(nextNode, currentSLAP, currentLevel-1, matching, guideLayers, used)
if found {
return slap, true
}
currentSLAP = currentSLAP[:len(currentSLAP)-1]
}
used[currentNode.ID] = false
return nil, false
}
func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers []NodeOrderedSet) {
used := make(map[int]bool)
currentLayer := NodeOrderedSet{}
for _, node := range bg.Left {
if matching.Free(node) {
used[node.ID] = true
currentLayer = append(currentLayer, node)
}
}
if len(currentLayer) == 0 {
return []NodeOrderedSet{}
}
guideLayers = append(guideLayers, currentLayer)
done := false
for !done {
lastLayer := currentLayer
currentLayer = NodeOrderedSet{}
if util.Odd(len(guideLayers)) {
for _, leftNode := range lastLayer {
for _, rightNode := range bg.Right {
if used[rightNode.ID] {
continue
}
edge, found := bg.Edges.FindByNodes(leftNode, rightNode)
if !found || matching.Contains(edge) {
continue
}
currentLayer = append(currentLayer, rightNode)
used[rightNode.ID] = true
if matching.Free(rightNode) {
done = true
}
}
}
} else {
for _, rightNode := range lastLayer {
for _, leftNode := range bg.Left {
if used[leftNode.ID] {
continue
}
edge, found := bg.Edges.FindByNodes(leftNode, rightNode)
if !found || !matching.Contains(edge) {
continue
}
currentLayer = append(currentLayer, leftNode)
used[leftNode.ID] = true
}
}
}
if len(currentLayer) == 0 {
return []NodeOrderedSet{}
}
guideLayers = append(guideLayers, currentLayer)
}
return
}

View File

@@ -0,0 +1,61 @@
package edge
import . "github.com/onsi/gomega/matchers/support/goraph/node"
type Edge struct {
Node1 int
Node2 int
}
type EdgeSet []Edge
func (ec EdgeSet) Free(node Node) bool {
for _, e := range ec {
if e.Node1 == node.ID || e.Node2 == node.ID {
return false
}
}
return true
}
func (ec EdgeSet) Contains(edge Edge) bool {
for _, e := range ec {
if e == edge {
return true
}
}
return false
}
func (ec EdgeSet) FindByNodes(node1, node2 Node) (Edge, bool) {
for _, e := range ec {
if (e.Node1 == node1.ID && e.Node2 == node2.ID) || (e.Node1 == node2.ID && e.Node2 == node1.ID) {
return e, true
}
}
return Edge{}, false
}
func (ec EdgeSet) SymmetricDifference(ec2 EdgeSet) EdgeSet {
edgesToInclude := make(map[Edge]bool)
for _, e := range ec {
edgesToInclude[e] = true
}
for _, e := range ec2 {
edgesToInclude[e] = !edgesToInclude[e]
}
result := EdgeSet{}
for e, include := range edgesToInclude {
if include {
result = append(result, e)
}
}
return result
}

View File

@@ -0,0 +1,8 @@
package node
type Node struct {
ID int
Value interface{}
}
type NodeOrderedSet []Node

View File

@@ -0,0 +1,7 @@
package util
import "math"
func Odd(n int) bool {
return math.Mod(float64(n), 2.0) == 1.0
}

182
vendor/github.com/onsi/gomega/matchers/type_support.go generated vendored Normal file
View File

@@ -0,0 +1,182 @@
/*
Gomega matchers
This package implements the Gomega matchers and does not typically need to be imported.
See the docs for Gomega for documentation on the matchers
http://onsi.github.io/gomega/
*/
// untested sections: 11
package matchers
import (
"encoding/json"
"fmt"
"reflect"
)
type omegaMatcher interface {
Match(actual interface{}) (success bool, err error)
FailureMessage(actual interface{}) (message string)
NegatedFailureMessage(actual interface{}) (message string)
}
func isBool(a interface{}) bool {
return reflect.TypeOf(a).Kind() == reflect.Bool
}
func isNumber(a interface{}) bool {
if a == nil {
return false
}
kind := reflect.TypeOf(a).Kind()
return reflect.Int <= kind && kind <= reflect.Float64
}
func isInteger(a interface{}) bool {
kind := reflect.TypeOf(a).Kind()
return reflect.Int <= kind && kind <= reflect.Int64
}
func isUnsignedInteger(a interface{}) bool {
kind := reflect.TypeOf(a).Kind()
return reflect.Uint <= kind && kind <= reflect.Uint64
}
func isFloat(a interface{}) bool {
kind := reflect.TypeOf(a).Kind()
return reflect.Float32 <= kind && kind <= reflect.Float64
}
func toInteger(a interface{}) int64 {
if isInteger(a) {
return reflect.ValueOf(a).Int()
} else if isUnsignedInteger(a) {
return int64(reflect.ValueOf(a).Uint())
} else if isFloat(a) {
return int64(reflect.ValueOf(a).Float())
}
panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a))
}
func toUnsignedInteger(a interface{}) uint64 {
if isInteger(a) {
return uint64(reflect.ValueOf(a).Int())
} else if isUnsignedInteger(a) {
return reflect.ValueOf(a).Uint()
} else if isFloat(a) {
return uint64(reflect.ValueOf(a).Float())
}
panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a))
}
func toFloat(a interface{}) float64 {
if isInteger(a) {
return float64(reflect.ValueOf(a).Int())
} else if isUnsignedInteger(a) {
return float64(reflect.ValueOf(a).Uint())
} else if isFloat(a) {
return reflect.ValueOf(a).Float()
}
panic(fmt.Sprintf("Expected a number! Got <%T> %#v", a, a))
}
func isError(a interface{}) bool {
_, ok := a.(error)
return ok
}
func isChan(a interface{}) bool {
if isNil(a) {
return false
}
return reflect.TypeOf(a).Kind() == reflect.Chan
}
func isMap(a interface{}) bool {
if a == nil {
return false
}
return reflect.TypeOf(a).Kind() == reflect.Map
}
func isArrayOrSlice(a interface{}) bool {
if a == nil {
return false
}
switch reflect.TypeOf(a).Kind() {
case reflect.Array, reflect.Slice:
return true
default:
return false
}
}
func isString(a interface{}) bool {
if a == nil {
return false
}
return reflect.TypeOf(a).Kind() == reflect.String
}
func toString(a interface{}) (string, bool) {
aString, isString := a.(string)
if isString {
return aString, true
}
aBytes, isBytes := a.([]byte)
if isBytes {
return string(aBytes), true
}
aStringer, isStringer := a.(fmt.Stringer)
if isStringer {
return aStringer.String(), true
}
aJSONRawMessage, isJSONRawMessage := a.(json.RawMessage)
if isJSONRawMessage {
return string(aJSONRawMessage), true
}
return "", false
}
func lengthOf(a interface{}) (int, bool) {
if a == nil {
return 0, false
}
switch reflect.TypeOf(a).Kind() {
case reflect.Map, reflect.Array, reflect.String, reflect.Chan, reflect.Slice:
return reflect.ValueOf(a).Len(), true
default:
return 0, false
}
}
func capOf(a interface{}) (int, bool) {
if a == nil {
return 0, false
}
switch reflect.TypeOf(a).Kind() {
case reflect.Array, reflect.Chan, reflect.Slice:
return reflect.ValueOf(a).Cap(), true
default:
return 0, false
}
}
func isNil(a interface{}) bool {
if a == nil {
return true
}
switch reflect.TypeOf(a).Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return reflect.ValueOf(a).IsNil()
}
return false
}

View File

@@ -0,0 +1,90 @@
package matchers
import (
"fmt"
"reflect"
"github.com/onsi/gomega/types"
)
type WithTransformMatcher struct {
// input
Transform interface{} // must be a function of one parameter that returns one value and an optional error
Matcher types.GomegaMatcher
// cached value
transformArgType reflect.Type
// state
transformedValue interface{}
}
// reflect.Type for error
var errorT = reflect.TypeOf((*error)(nil)).Elem()
func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) *WithTransformMatcher {
if transform == nil {
panic("transform function cannot be nil")
}
txType := reflect.TypeOf(transform)
if txType.NumIn() != 1 {
panic("transform function must have 1 argument")
}
if numout := txType.NumOut(); numout != 1 {
if numout != 2 || !txType.Out(1).AssignableTo(errorT) {
panic("transform function must either have 1 return value, or 1 return value plus 1 error value")
}
}
return &WithTransformMatcher{
Transform: transform,
Matcher: matcher,
transformArgType: reflect.TypeOf(transform).In(0),
}
}
func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) {
// prepare a parameter to pass to the Transform function
var param reflect.Value
if actual != nil && reflect.TypeOf(actual).AssignableTo(m.transformArgType) {
// The dynamic type of actual is compatible with the transform argument.
param = reflect.ValueOf(actual)
} else if actual == nil && m.transformArgType.Kind() == reflect.Interface {
// The dynamic type of actual is unknown, so there's no way to make its
// reflect.Value. Create a nil of the transform argument, which is known.
param = reflect.Zero(m.transformArgType)
} else {
return false, fmt.Errorf("Transform function expects '%s' but we have '%T'", m.transformArgType, actual)
}
// call the Transform function with `actual`
fn := reflect.ValueOf(m.Transform)
result := fn.Call([]reflect.Value{param})
if len(result) == 2 {
if !result[1].IsNil() {
return false, fmt.Errorf("Transform function failed: %s", result[1].Interface().(error).Error())
}
}
m.transformedValue = result[0].Interface() // expect exactly one value
return m.Matcher.Match(m.transformedValue)
}
func (m *WithTransformMatcher) FailureMessage(_ interface{}) (message string) {
return m.Matcher.FailureMessage(m.transformedValue)
}
func (m *WithTransformMatcher) NegatedFailureMessage(_ interface{}) (message string) {
return m.Matcher.NegatedFailureMessage(m.transformedValue)
}
func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool {
// TODO: Maybe this should always just return true? (Only an issue for non-deterministic transformers.)
//
// Querying the next matcher is fine if the transformer always will return the same value.
// But if the transformer is non-deterministic and returns a different value each time, then there
// is no point in querying the next matcher, since it can only comment on the last transformed value.
return types.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
}