|  | 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)) | 
|  | } | 
|  |  | 
|  | assignable := channelType.Elem().AssignableTo(argType.Elem()) | 
|  | if !assignable { | 
|  | return false, fmt.Errorf("Cannot assign a value from the channel:\n%s\nTo:\n%s", format.Object(actual, 1), format.Object(matcher.Arg, 1)) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | winnerIndex, value, open := reflect.Select([]reflect.SelectCase{ | 
|  | reflect.SelectCase{Dir: reflect.SelectRecv, Chan: channelValue}, | 
|  | reflect.SelectCase{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()) | 
|  | } else { | 
|  | return false, nil | 
|  | } | 
|  | } | 
|  |  | 
|  | if didReceive { | 
|  | if matcher.Arg != nil { | 
|  | outValue := reflect.ValueOf(matcher.Arg) | 
|  | reflect.Indirect(outValue).Set(value) | 
|  | } | 
|  |  | 
|  | return true, nil | 
|  | } else { | 
|  | 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." | 
|  | } else { | 
|  | 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." | 
|  | } else { | 
|  | return format.Message(actual, "not to receive anything."+closedAddendum) | 
|  | } | 
|  | } | 
|  |  | 
|  | func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { | 
|  | if !isChan(actual) { | 
|  | return false | 
|  | } | 
|  |  | 
|  | return !matcher.channelClosed | 
|  | } |