blob: e9ce243c4d6a18cd05e29a2cd29cf7e388e62b16 [file] [log] [blame]
package ipc
import (
"errors"
"fmt"
"sync"
"v.io/core/veyron2/context"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/security"
"v.io/core/veyron2/vdl/valconv"
"v.io/core/veyron2/vlog"
"v.io/core/veyron2/vom"
)
// NewSimpleClient creates a new mocked ipc client where the given map of method name
// to outputs is used for evaluating the method calls.
// It also adds some testing features such as counters for number of times a method is called
func NewSimpleClient(methodsResults map[string][]interface{}) *SimpleMockClient {
return &SimpleMockClient{
results: methodsResults,
timesCalled: make(map[string]int),
}
}
// SimpleMockClient implements ipc.Client
type SimpleMockClient struct {
// Protects timesCalled
sync.Mutex
// results is a map of method names to results
results map[string][]interface{}
// timesCalled is a counter for number of times StartCall is called on a specific method name
timesCalled map[string]int
}
// TimesCalled returns number of times the given method has been called.
func (c *SimpleMockClient) TimesCalled(method string) int {
return c.timesCalled[method]
}
// IPCBindOpt Implements ipc.Client
func (c *SimpleMockClient) IPCBindOpt() {
//nologcall
}
// StartCall Implements ipc.Client
func (c *SimpleMockClient) StartCall(ctx *context.T, name, method string, args []interface{}, opts ...ipc.CallOpt) (ipc.Call, error) {
defer vlog.LogCall()()
results, ok := c.results[method]
if !ok {
return nil, errors.New(fmt.Sprintf("method %s not found", method))
}
// Copy the results so that they can be modified without effecting the original.
// This must be done via vom encode and decode rather than a direct deep copy because (among other reasons)
// reflect-based deep copy on vdl.Type objects will fail because of their private fields. This is not a problem with vom
// as it manually creates the type objects. It is also more realistic to use the same mechanism as the ultimate calls.
vomBytes, err := vom.Encode(results)
if err != nil {
panic(fmt.Sprintf("Error copying value with vom (failed on encode): %v", err))
}
var copiedResults []interface{}
if err := vom.Decode(vomBytes, &copiedResults); err != nil {
panic(fmt.Sprintf("Error copying value with vom (failed on decode): %v", err))
}
clientCall := mockCall{
results: copiedResults,
}
c.Lock()
c.timesCalled[method]++
c.Unlock()
return &clientCall, nil
}
// Close implements ipc.Client
func (*SimpleMockClient) Close() {
defer vlog.LogCall()()
}
// mockCall implements ipc.Call
type mockCall struct {
mockStream
results []interface{}
}
// Cancel implements ipc.Call
func (*mockCall) Cancel() {
defer vlog.LogCall()()
}
// CloseSend implements ipc.Call
func (*mockCall) CloseSend() error {
defer vlog.LogCall()()
return nil
}
// Finish implements ipc.Call
func (mc *mockCall) Finish(resultptrs ...interface{}) error {
defer vlog.LogCall()()
if got, want := len(resultptrs), len(mc.results); got != want {
return errors.New(fmt.Sprintf("wrong number of output results; expected resultptrs of size %d but got %d", want, got))
}
for ax, res := range resultptrs {
if mc.results[ax] != nil {
if err := valconv.Convert(res, mc.results[ax]); err != nil {
panic(fmt.Sprintf("Error converting out argument %#v: %v", mc.results[ax], err))
}
}
}
return nil
}
// RemoteBlessings implements ipc.Call
func (*mockCall) RemoteBlessings() ([]string, security.Blessings) {
return []string{}, nil
}
//mockStream implements ipc.Stream
type mockStream struct{}
//Send implements ipc.Stream
func (*mockStream) Send(interface{}) error {
defer vlog.LogCall()()
return nil
}
//Recv implements ipc.Stream
func (*mockStream) Recv(interface{}) error {
defer vlog.LogCall()()
return nil
}