be_sent_matcher.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. package matchers
  2. import (
  3. "fmt"
  4. "reflect"
  5. "github.com/onsi/gomega/format"
  6. )
  7. type BeSentMatcher struct {
  8. Arg interface{}
  9. channelClosed bool
  10. }
  11. func (matcher *BeSentMatcher) Match(actual interface{}) (success bool, err error) {
  12. if !isChan(actual) {
  13. return false, fmt.Errorf("BeSent expects a channel. Got:\n%s", format.Object(actual, 1))
  14. }
  15. channelType := reflect.TypeOf(actual)
  16. channelValue := reflect.ValueOf(actual)
  17. if channelType.ChanDir() == reflect.RecvDir {
  18. return false, fmt.Errorf("BeSent matcher cannot be passed a receive-only channel. Got:\n%s", format.Object(actual, 1))
  19. }
  20. argType := reflect.TypeOf(matcher.Arg)
  21. assignable := argType.AssignableTo(channelType.Elem())
  22. if !assignable {
  23. 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))
  24. }
  25. argValue := reflect.ValueOf(matcher.Arg)
  26. defer func() {
  27. if e := recover(); e != nil {
  28. success = false
  29. err = fmt.Errorf("Cannot send to a closed channel")
  30. matcher.channelClosed = true
  31. }
  32. }()
  33. winnerIndex, _, _ := reflect.Select([]reflect.SelectCase{
  34. {Dir: reflect.SelectSend, Chan: channelValue, Send: argValue},
  35. {Dir: reflect.SelectDefault},
  36. })
  37. var didSend bool
  38. if winnerIndex == 0 {
  39. didSend = true
  40. }
  41. return didSend, nil
  42. }
  43. func (matcher *BeSentMatcher) FailureMessage(actual interface{}) (message string) {
  44. return format.Message(actual, "to send:", matcher.Arg)
  45. }
  46. func (matcher *BeSentMatcher) NegatedFailureMessage(actual interface{}) (message string) {
  47. return format.Message(actual, "not to send:", matcher.Arg)
  48. }
  49. func (matcher *BeSentMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
  50. if !isChan(actual) {
  51. return false
  52. }
  53. return !matcher.channelClosed
  54. }