blob: d7c32233ec429fc4c5de37187efbac49c19b8d0c [file] [log] [blame]
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{
reflect.SelectCase{Dir: reflect.SelectSend, Chan: channelValue, Send: argValue},
reflect.SelectCase{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
}