...

Source file src/github.com/onsi/gomega/matchers/receive_matcher.go

Documentation: github.com/onsi/gomega/matchers

     1  // untested sections: 3
     2  
     3  package matchers
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"reflect"
     9  
    10  	"github.com/onsi/gomega/format"
    11  )
    12  
    13  type ReceiveMatcher struct {
    14  	Args          []interface{}
    15  	receivedValue reflect.Value
    16  	channelClosed bool
    17  }
    18  
    19  func (matcher *ReceiveMatcher) Match(actual interface{}) (success bool, err error) {
    20  	if !isChan(actual) {
    21  		return false, fmt.Errorf("ReceiveMatcher expects a channel.  Got:\n%s", format.Object(actual, 1))
    22  	}
    23  
    24  	channelType := reflect.TypeOf(actual)
    25  	channelValue := reflect.ValueOf(actual)
    26  
    27  	if channelType.ChanDir() == reflect.SendDir {
    28  		return false, fmt.Errorf("ReceiveMatcher matcher cannot be passed a send-only channel.  Got:\n%s", format.Object(actual, 1))
    29  	}
    30  
    31  	var subMatcher omegaMatcher
    32  	var hasSubMatcher bool
    33  	var resultReference interface{}
    34  
    35  	// Valid arg formats are as follows, always with optional POINTER before
    36  	// optional MATCHER:
    37  	//   - Receive()
    38  	//   - Receive(POINTER)
    39  	//   - Receive(MATCHER)
    40  	//   - Receive(POINTER, MATCHER)
    41  	args := matcher.Args
    42  	if len(args) > 0 {
    43  		arg := args[0]
    44  		_, isSubMatcher := arg.(omegaMatcher)
    45  		if !isSubMatcher && reflect.ValueOf(arg).Kind() == reflect.Ptr {
    46  			// Consume optional POINTER arg first, if it ain't no matcher ;)
    47  			resultReference = arg
    48  			args = args[1:]
    49  		}
    50  	}
    51  	if len(args) > 0 {
    52  		arg := args[0]
    53  		subMatcher, hasSubMatcher = arg.(omegaMatcher)
    54  		if !hasSubMatcher {
    55  			// At this point we assume the dev user wanted to assign a received
    56  			// value, so [POINTER,]MATCHER.
    57  			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(arg, 1))
    58  		}
    59  		// Consume optional MATCHER arg.
    60  		args = args[1:]
    61  	}
    62  	if len(args) > 0 {
    63  		// If there are still args present, reject all.
    64  		return false, errors.New("Receive matcher expects at most an optional pointer and/or an optional matcher")
    65  	}
    66  
    67  	winnerIndex, value, open := reflect.Select([]reflect.SelectCase{
    68  		{Dir: reflect.SelectRecv, Chan: channelValue},
    69  		{Dir: reflect.SelectDefault},
    70  	})
    71  
    72  	var closed bool
    73  	var didReceive bool
    74  	if winnerIndex == 0 {
    75  		closed = !open
    76  		didReceive = open
    77  	}
    78  	matcher.channelClosed = closed
    79  
    80  	if closed {
    81  		return false, nil
    82  	}
    83  
    84  	if hasSubMatcher {
    85  		if !didReceive {
    86  			return false, nil
    87  		}
    88  		matcher.receivedValue = value
    89  		if match, err := subMatcher.Match(matcher.receivedValue.Interface()); err != nil || !match {
    90  			return match, err
    91  		}
    92  		// if we received a match, then fall through in order to handle an
    93  		// optional assignment of the received value to the specified reference.
    94  	}
    95  
    96  	if didReceive {
    97  		if resultReference != nil {
    98  			outValue := reflect.ValueOf(resultReference)
    99  
   100  			if value.Type().AssignableTo(outValue.Elem().Type()) {
   101  				outValue.Elem().Set(value)
   102  				return true, nil
   103  			}
   104  			if value.Type().Kind() == reflect.Interface && value.Elem().Type().AssignableTo(outValue.Elem().Type()) {
   105  				outValue.Elem().Set(value.Elem())
   106  				return true, nil
   107  			} else {
   108  				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(resultReference, 1))
   109  			}
   110  
   111  		}
   112  
   113  		return true, nil
   114  	}
   115  	return false, nil
   116  }
   117  
   118  func (matcher *ReceiveMatcher) FailureMessage(actual interface{}) (message string) {
   119  	var matcherArg interface{}
   120  	if len(matcher.Args) > 0 {
   121  		matcherArg = matcher.Args[len(matcher.Args)-1]
   122  	}
   123  	subMatcher, hasSubMatcher := (matcherArg).(omegaMatcher)
   124  
   125  	closedAddendum := ""
   126  	if matcher.channelClosed {
   127  		closedAddendum = " The channel is closed."
   128  	}
   129  
   130  	if hasSubMatcher {
   131  		if matcher.receivedValue.IsValid() {
   132  			return subMatcher.FailureMessage(matcher.receivedValue.Interface())
   133  		}
   134  		return "When passed a matcher, ReceiveMatcher's channel *must* receive something."
   135  	}
   136  	return format.Message(actual, "to receive something."+closedAddendum)
   137  }
   138  
   139  func (matcher *ReceiveMatcher) NegatedFailureMessage(actual interface{}) (message string) {
   140  	var matcherArg interface{}
   141  	if len(matcher.Args) > 0 {
   142  		matcherArg = matcher.Args[len(matcher.Args)-1]
   143  	}
   144  	subMatcher, hasSubMatcher := (matcherArg).(omegaMatcher)
   145  
   146  	closedAddendum := ""
   147  	if matcher.channelClosed {
   148  		closedAddendum = " The channel is closed."
   149  	}
   150  
   151  	if hasSubMatcher {
   152  		if matcher.receivedValue.IsValid() {
   153  			return subMatcher.NegatedFailureMessage(matcher.receivedValue.Interface())
   154  		}
   155  		return "When passed a matcher, ReceiveMatcher's channel *must* receive something."
   156  	}
   157  	return format.Message(actual, "not to receive anything."+closedAddendum)
   158  }
   159  
   160  func (matcher *ReceiveMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
   161  	if !isChan(actual) {
   162  		return false
   163  	}
   164  
   165  	return !matcher.channelClosed
   166  }
   167  

View as plain text