| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This package aids in the testing of code that uses channels. |
| package script |
| |
| import ( |
| "fmt" |
| "os" |
| "rand" |
| "reflect" |
| "strings" |
| ) |
| |
| // An Event is an element in a partially ordered set that either sends a value |
| // to a channel or expects a value from a channel. |
| type Event struct { |
| name string |
| occurred bool |
| predecessors []*Event |
| action action |
| } |
| |
| type action interface { |
| // getSend returns nil if the action is not a send action. |
| getSend() sendAction |
| // getRecv returns nil if the action is not a receive action. |
| getRecv() recvAction |
| // getChannel returns the channel that the action operates on. |
| getChannel() interface{} |
| } |
| |
| type recvAction interface { |
| recvMatch(interface{}) bool |
| } |
| |
| type sendAction interface { |
| send() |
| } |
| |
| // isReady returns true if all the predecessors of an Event have occurred. |
| func (e Event) isReady() bool { |
| for _, predecessor := range e.predecessors { |
| if !predecessor.occurred { |
| return false |
| } |
| } |
| |
| return true |
| } |
| |
| // A Recv action reads a value from a channel and uses reflect.DeepMatch to |
| // compare it with an expected value. |
| type Recv struct { |
| Channel interface{} |
| Expected interface{} |
| } |
| |
| func (r Recv) getRecv() recvAction { return r } |
| |
| func (Recv) getSend() sendAction { return nil } |
| |
| func (r Recv) getChannel() interface{} { return r.Channel } |
| |
| func (r Recv) recvMatch(chanEvent interface{}) bool { |
| c, ok := chanEvent.(channelRecv) |
| if !ok || c.channel != r.Channel { |
| return false |
| } |
| |
| return reflect.DeepEqual(c.value, r.Expected) |
| } |
| |
| // A RecvMatch action reads a value from a channel and calls a function to |
| // determine if the value matches. |
| type RecvMatch struct { |
| Channel interface{} |
| Match func(interface{}) bool |
| } |
| |
| func (r RecvMatch) getRecv() recvAction { return r } |
| |
| func (RecvMatch) getSend() sendAction { return nil } |
| |
| func (r RecvMatch) getChannel() interface{} { return r.Channel } |
| |
| func (r RecvMatch) recvMatch(chanEvent interface{}) bool { |
| c, ok := chanEvent.(channelRecv) |
| if !ok || c.channel != r.Channel { |
| return false |
| } |
| |
| return r.Match(c.value) |
| } |
| |
| // A Closed action matches if the given channel is closed. The closing is |
| // treated as an event, not a state, thus Closed will only match once for a |
| // given channel. |
| type Closed struct { |
| Channel interface{} |
| } |
| |
| func (r Closed) getRecv() recvAction { return r } |
| |
| func (Closed) getSend() sendAction { return nil } |
| |
| func (r Closed) getChannel() interface{} { return r.Channel } |
| |
| func (r Closed) recvMatch(chanEvent interface{}) bool { |
| c, ok := chanEvent.(channelClosed) |
| if !ok || c.channel != r.Channel { |
| return false |
| } |
| |
| return true |
| } |
| |
| // A Send action sends a value to a channel. The value must match the |
| // type of the channel exactly unless the channel if of type chan interface{}. |
| type Send struct { |
| Channel interface{} |
| Value interface{} |
| } |
| |
| func (Send) getRecv() recvAction { return nil } |
| |
| func (s Send) getSend() sendAction { return s } |
| |
| func (s Send) getChannel() interface{} { return s.Channel } |
| |
| type empty struct { |
| x interface{} |
| } |
| |
| func newEmptyInterface(e empty) reflect.Value { |
| return reflect.ValueOf(e).Field(0) |
| } |
| |
| func (s Send) send() { |
| // With reflect.ChanValue.Send, we must match the types exactly. So, if |
| // s.Channel is a chan interface{} we convert s.Value to an interface{} |
| // first. |
| c := reflect.ValueOf(s.Channel) |
| var v reflect.Value |
| if iface := c.Type().Elem(); iface.Kind() == reflect.Interface && iface.NumMethod() == 0 { |
| v = newEmptyInterface(empty{s.Value}) |
| } else { |
| v = reflect.ValueOf(s.Value) |
| } |
| c.Send(v) |
| } |
| |
| // A Close action closes the given channel. |
| type Close struct { |
| Channel interface{} |
| } |
| |
| func (Close) getRecv() recvAction { return nil } |
| |
| func (s Close) getSend() sendAction { return s } |
| |
| func (s Close) getChannel() interface{} { return s.Channel } |
| |
| func (s Close) send() { reflect.ValueOf(s.Channel).Close() } |
| |
| // A ReceivedUnexpected error results if no active Events match a value |
| // received from a channel. |
| type ReceivedUnexpected struct { |
| Value interface{} |
| ready []*Event |
| } |
| |
| func (r ReceivedUnexpected) String() string { |
| names := make([]string, len(r.ready)) |
| for i, v := range r.ready { |
| names[i] = v.name |
| } |
| return fmt.Sprintf("received unexpected value on one of the channels: %#v. Runnable events: %s", r.Value, strings.Join(names, ", ")) |
| } |
| |
| // A SetupError results if there is a error with the configuration of a set of |
| // Events. |
| type SetupError string |
| |
| func (s SetupError) String() string { return string(s) } |
| |
| func NewEvent(name string, predecessors []*Event, action action) *Event { |
| e := &Event{name, false, predecessors, action} |
| return e |
| } |
| |
| // Given a set of Events, Perform repeatedly iterates over the set and finds the |
| // subset of ready Events (that is, all of their predecessors have |
| // occurred). From that subset, it pseudo-randomly selects an Event to perform. |
| // If the Event is a send event, the send occurs and Perform recalculates the ready |
| // set. If the event is a receive event, Perform waits for a value from any of the |
| // channels that are contained in any of the events. That value is then matched |
| // against the ready events. The first event that matches is considered to |
| // have occurred and Perform recalculates the ready set. |
| // |
| // Perform continues this until all Events have occurred. |
| // |
| // Note that uncollected goroutines may still be reading from any of the |
| // channels read from after Perform returns. |
| // |
| // For example, consider the problem of testing a function that reads values on |
| // one channel and echos them to two output channels. To test this we would |
| // create three events: a send event and two receive events. Each of the |
| // receive events must list the send event as a predecessor but there is no |
| // ordering between the receive events. |
| // |
| // send := NewEvent("send", nil, Send{c, 1}) |
| // recv1 := NewEvent("recv 1", []*Event{send}, Recv{c, 1}) |
| // recv2 := NewEvent("recv 2", []*Event{send}, Recv{c, 1}) |
| // Perform(0, []*Event{send, recv1, recv2}) |
| // |
| // At first, only the send event would be in the ready set and thus Perform will |
| // send a value to the input channel. Now the two receive events are ready and |
| // Perform will match each of them against the values read from the output channels. |
| // |
| // It would be invalid to list one of the receive events as a predecessor of |
| // the other. At each receive step, all the receive channels are considered, |
| // thus Perform may see a value from a channel that is not in the current ready |
| // set and fail. |
| func Perform(seed int64, events []*Event) (err os.Error) { |
| r := rand.New(rand.NewSource(seed)) |
| |
| channels, err := getChannels(events) |
| if err != nil { |
| return |
| } |
| multiplex := make(chan interface{}) |
| for _, channel := range channels { |
| go recvValues(multiplex, channel) |
| } |
| |
| Outer: |
| for { |
| ready, err := readyEvents(events) |
| if err != nil { |
| return err |
| } |
| |
| if len(ready) == 0 { |
| // All events occurred. |
| break |
| } |
| |
| event := ready[r.Intn(len(ready))] |
| if send := event.action.getSend(); send != nil { |
| send.send() |
| event.occurred = true |
| continue |
| } |
| |
| v := <-multiplex |
| for _, event := range ready { |
| if recv := event.action.getRecv(); recv != nil && recv.recvMatch(v) { |
| event.occurred = true |
| continue Outer |
| } |
| } |
| |
| return ReceivedUnexpected{v, ready} |
| } |
| |
| return nil |
| } |
| |
| // getChannels returns all the channels listed in any receive events. |
| func getChannels(events []*Event) ([]interface{}, os.Error) { |
| channels := make([]interface{}, len(events)) |
| |
| j := 0 |
| for _, event := range events { |
| if recv := event.action.getRecv(); recv == nil { |
| continue |
| } |
| c := event.action.getChannel() |
| if reflect.ValueOf(c).Kind() != reflect.Chan { |
| return nil, SetupError("one of the channel values is not a channel") |
| } |
| |
| duplicate := false |
| for _, other := range channels[0:j] { |
| if c == other { |
| duplicate = true |
| break |
| } |
| } |
| |
| if !duplicate { |
| channels[j] = c |
| j++ |
| } |
| } |
| |
| return channels[0:j], nil |
| } |
| |
| // recvValues is a multiplexing helper function. It reads values from the given |
| // channel repeatedly, wrapping them up as either a channelRecv or |
| // channelClosed structure, and forwards them to the multiplex channel. |
| func recvValues(multiplex chan<- interface{}, channel interface{}) { |
| c := reflect.ValueOf(channel) |
| |
| for { |
| v, ok := c.Recv() |
| if !ok { |
| multiplex <- channelClosed{channel} |
| return |
| } |
| |
| multiplex <- channelRecv{channel, v.Interface()} |
| } |
| } |
| |
| type channelClosed struct { |
| channel interface{} |
| } |
| |
| type channelRecv struct { |
| channel interface{} |
| value interface{} |
| } |
| |
| // readyEvents returns the subset of events that are ready. |
| func readyEvents(events []*Event) ([]*Event, os.Error) { |
| ready := make([]*Event, len(events)) |
| |
| j := 0 |
| eventsWaiting := false |
| for _, event := range events { |
| if event.occurred { |
| continue |
| } |
| |
| eventsWaiting = true |
| if event.isReady() { |
| ready[j] = event |
| j++ |
| } |
| } |
| |
| if j == 0 && eventsWaiting { |
| names := make([]string, len(events)) |
| for _, event := range events { |
| if event.occurred { |
| continue |
| } |
| names[j] = event.name |
| } |
| |
| return nil, SetupError("dependency cycle in events. These events are waiting to run but cannot: " + strings.Join(names, ", ")) |
| } |
| |
| return ready[0:j], nil |
| } |