blob: a50fc678eeb0c4d927ceed2f0288427d02c08696 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package lib
import (
"errors"
"fmt"
"sync"
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/vdl"
"v.io/v23/vom"
"v.io/x/lib/vlog"
)
type clientWithTimesCalled interface {
rpc.Client
TimesCalled(method string) int
}
// NewSimpleClient creates a new mocked rpc 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{}) clientWithTimesCalled {
return &simpleMockClient{
results: methodsResults,
timesCalled: make(map[string]int),
}
}
// simpleMockClient implements rpc.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]
}
// StartCall Implements rpc.Client
func (c *simpleMockClient) StartCall(ctx *context.T, name, method string, args []interface{}, opts ...rpc.CallOpt) (rpc.ClientCall, error) {
defer vlog.LogCall()()
results, ok := c.results[method]
if !ok {
return nil, fmt.Errorf("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
}
func (c *simpleMockClient) Call(ctx *context.T, name, method string, inArgs, outArgs []interface{}, callOpts ...rpc.CallOpt) error {
call, err := c.StartCall(ctx, name, method, inArgs, callOpts...)
if err != nil {
return err
}
return call.Finish(outArgs...)
}
// Close implements rpc.Client
func (*simpleMockClient) Close() {
defer vlog.LogCall()()
}
// mockCall implements rpc.ClientCall
type mockCall struct {
mockStream
results []interface{}
}
// Cancel implements rpc.ClientCall
func (*mockCall) Cancel() {
defer vlog.LogCall()()
}
// CloseSend implements rpc.ClientCall
func (*mockCall) CloseSend() error {
defer vlog.LogCall()()
return nil
}
// Finish implements rpc.ClientCall
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 := vdl.Convert(res, mc.results[ax]); err != nil {
panic(fmt.Sprintf("Error converting out argument %#v: %v", mc.results[ax], err))
}
}
}
return nil
}
// RemoteBlessings implements rpc.ClientCall
func (*mockCall) RemoteBlessings() ([]string, security.Blessings) {
return []string{}, security.Blessings{}
}
//mockStream implements rpc.Stream
type mockStream struct{}
//Send implements rpc.Stream
func (*mockStream) Send(interface{}) error {
defer vlog.LogCall()()
return nil
}
//Recv implements rpc.Stream
func (*mockStream) Recv(interface{}) error {
defer vlog.LogCall()()
return nil
}