Plan 9 from Bell Labs’s /usr/web/sources/contrib/ericvh/go-plan9/src/pkg/testing/script/script.go

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// 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 }

func newEmptyInterface(args ...) reflect.Value {
	return reflect.NewValue(args).(*reflect.StructValue).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.NewValue(s.Channel).(*reflect.ChanValue);
	var v reflect.Value;
	if iface, ok := c.Type().(*reflect.ChanType).Elem().(*reflect.InterfaceType); ok && iface.NumMethod() == 0 {
		v = newEmptyInterface(s.Value)
	} else {
		v = reflect.NewValue(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.NewValue(s.Channel).(*reflect.ChanValue).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 _, ok := reflect.NewValue(c).(*reflect.ChanValue); !ok {
			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.NewValue(channel).(*reflect.ChanValue);

	for {
		v := c.Recv();
		if c.Closed() {
			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;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.