Path: blob/main/vendor/github.com/onsi/gomega/matchers/receive_matcher.go
2880 views
// untested sections: 312package matchers34import (5"errors"6"fmt"7"reflect"89"github.com/onsi/gomega/format"10)1112type ReceiveMatcher struct {13Args []any14receivedValue reflect.Value15channelClosed bool16}1718func (matcher *ReceiveMatcher) Match(actual any) (success bool, err error) {19if !isChan(actual) {20return false, fmt.Errorf("ReceiveMatcher expects a channel. Got:\n%s", format.Object(actual, 1))21}2223channelType := reflect.TypeOf(actual)24channelValue := reflect.ValueOf(actual)2526if channelType.ChanDir() == reflect.SendDir {27return false, fmt.Errorf("ReceiveMatcher matcher cannot be passed a send-only channel. Got:\n%s", format.Object(actual, 1))28}2930var subMatcher omegaMatcher31var hasSubMatcher bool32var resultReference any3334// Valid arg formats are as follows, always with optional POINTER before35// optional MATCHER:36// - Receive()37// - Receive(POINTER)38// - Receive(MATCHER)39// - Receive(POINTER, MATCHER)40args := matcher.Args41if len(args) > 0 {42arg := args[0]43_, isSubMatcher := arg.(omegaMatcher)44if !isSubMatcher && reflect.ValueOf(arg).Kind() == reflect.Ptr {45// Consume optional POINTER arg first, if it ain't no matcher ;)46resultReference = arg47args = args[1:]48}49}50if len(args) > 0 {51arg := args[0]52subMatcher, hasSubMatcher = arg.(omegaMatcher)53if !hasSubMatcher {54// At this point we assume the dev user wanted to assign a received55// value, so [POINTER,]MATCHER.56return 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(arg, 1))57}58// Consume optional MATCHER arg.59args = args[1:]60}61if len(args) > 0 {62// If there are still args present, reject all.63return false, errors.New("Receive matcher expects at most an optional pointer and/or an optional matcher")64}6566winnerIndex, value, open := reflect.Select([]reflect.SelectCase{67{Dir: reflect.SelectRecv, Chan: channelValue},68{Dir: reflect.SelectDefault},69})7071var closed bool72var didReceive bool73if winnerIndex == 0 {74closed = !open75didReceive = open76}77matcher.channelClosed = closed7879if closed {80return false, nil81}8283if hasSubMatcher {84if !didReceive {85return false, nil86}87matcher.receivedValue = value88if match, err := subMatcher.Match(matcher.receivedValue.Interface()); err != nil || !match {89return match, err90}91// if we received a match, then fall through in order to handle an92// optional assignment of the received value to the specified reference.93}9495if didReceive {96if resultReference != nil {97outValue := reflect.ValueOf(resultReference)9899if value.Type().AssignableTo(outValue.Elem().Type()) {100outValue.Elem().Set(value)101return true, nil102}103if value.Type().Kind() == reflect.Interface && value.Elem().Type().AssignableTo(outValue.Elem().Type()) {104outValue.Elem().Set(value.Elem())105return true, nil106} else {107return 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(resultReference, 1))108}109110}111112return true, nil113}114return false, nil115}116117func (matcher *ReceiveMatcher) FailureMessage(actual any) (message string) {118var matcherArg any119if len(matcher.Args) > 0 {120matcherArg = matcher.Args[len(matcher.Args)-1]121}122subMatcher, hasSubMatcher := (matcherArg).(omegaMatcher)123124closedAddendum := ""125if matcher.channelClosed {126closedAddendum = " The channel is closed."127}128129if hasSubMatcher {130if matcher.receivedValue.IsValid() {131return subMatcher.FailureMessage(matcher.receivedValue.Interface())132}133return "When passed a matcher, ReceiveMatcher's channel *must* receive something."134}135return format.Message(actual, "to receive something."+closedAddendum)136}137138func (matcher *ReceiveMatcher) NegatedFailureMessage(actual any) (message string) {139var matcherArg any140if len(matcher.Args) > 0 {141matcherArg = matcher.Args[len(matcher.Args)-1]142}143subMatcher, hasSubMatcher := (matcherArg).(omegaMatcher)144145closedAddendum := ""146if matcher.channelClosed {147closedAddendum = " The channel is closed."148}149150if hasSubMatcher {151if matcher.receivedValue.IsValid() {152return subMatcher.NegatedFailureMessage(matcher.receivedValue.Interface())153}154return "When passed a matcher, ReceiveMatcher's channel *must* receive something."155}156return format.Message(actual, "not to receive anything."+closedAddendum)157}158159func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual any) bool {160if !isChan(actual) {161return false162}163164return !matcher.channelClosed165}166167168