blob: b78a56fbcd30345f5d938324492ca31c3777e491 [file] [log] [blame]
package ipc
import (
"bytes"
"errors"
"fmt"
"testing"
"veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/verror"
)
func init() { testutil.Init() }
// newTestFlows returns the two ends of a bidirectional flow. Each end has its
// own bookkeeping, to allow testing of method calls.
func newTestFlows() (*testFlow, *testFlow) {
var (
p0, p1 = sectest.NewPrincipal("p0"), sectest.NewPrincipal("p1")
blessing0, blessing1 = p0.BlessingStore().Default(), p1.BlessingStore().Default()
b0, b1 = new(bytes.Buffer), new(bytes.Buffer)
)
return &testFlow{r: b0, w: b1, p: p0, lb: blessing0, rb: blessing1}, &testFlow{r: b1, w: b0, p: p1, lb: blessing1, rb: blessing0}
}
type testFlow struct {
r, w *bytes.Buffer
p security.Principal
lb, rb security.Blessings
numCloseCalls int
errClose error
}
func (f *testFlow) Read(b []byte) (int, error) { return f.r.Read(b) }
func (f *testFlow) Write(b []byte) (int, error) { return f.w.Write(b) }
func (f *testFlow) LocalEndpoint() naming.Endpoint { return nil }
func (f *testFlow) RemoteEndpoint() naming.Endpoint { return nil }
func (f *testFlow) LocalPrincipal() security.Principal { return f.p }
func (f *testFlow) LocalBlessings() security.Blessings { return f.lb }
func (f *testFlow) RemoteBlessings() security.Blessings { return f.rb }
func (f *testFlow) SetDeadline(<-chan struct{}) {}
func (f *testFlow) IsClosed() bool { return false }
func (f *testFlow) Closed() <-chan struct{} { return nil }
func (f *testFlow) Cancel() {}
func (f *testFlow) Close() error {
f.numCloseCalls++
return f.errClose
}
// testDisp implements a simple test dispatcher, that uses the newInvoker
// factory function to create an underlying invoker on each Lookup.
type testDisp struct {
newInvoker func(suffix string) ipc.Invoker
}
func (td testDisp) Lookup(suffix, method string) (ipc.Invoker, security.Authorizer, error) {
return td.newInvoker(suffix), testServerAuthorizer{}, nil
}
// closureInvoker serves a method with no user args or results:
// func(ipc.ServerCall) error
type closureInvoker struct{ suffix string }
func newClosureInvoker(suffix string) ipc.Invoker {
return closureInvoker{suffix}
}
func (closureInvoker) Prepare(method string, numArgs int) (argptrs []interface{}, label security.Label, err error) {
return nil, security.AdminLabel, nil
}
func (inv closureInvoker) Invoke(method string, call ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
if inv.suffix == "" {
return nil, nil
}
return nil, errors.New(inv.suffix)
}
// echoInvoker serves a method that takes a string and echoes it:
// func(_ ServerCall, arg string) (string, error)
type echoInvoker struct{ suffix string }
func newEchoInvoker(suffix string) ipc.Invoker {
return echoInvoker{suffix}
}
func (echoInvoker) Prepare(method string, numArgs int) (argptrs []interface{}, label security.Label, err error) {
var arg string
return []interface{}{&arg}, security.AdminLabel, nil
}
func (inv echoInvoker) Invoke(method string, call ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
result := fmt.Sprintf("method:%q,suffix:%q,arg:%q", method, inv.suffix, *argptrs[0].(*string))
return []interface{}{result}, nil
}
func TestFlowClientServer(t *testing.T) {
type v []interface{}
type testcase struct {
suffix string
method string
args []interface{}
expect []interface{}
err error
}
tests := []testcase{
{"echo", "A", v{""}, v{`method:"A",suffix:"echo",arg:""`}, nil},
{"echo", "B", v{"foo"}, v{`method:"B",suffix:"echo",arg:"foo"`}, nil},
{"echo/abc", "C", v{""}, v{`method:"C",suffix:"echo/abc",arg:""`}, nil},
{"echo/abc", "D", v{"foo"}, v{`method:"D",suffix:"echo/abc",arg:"foo"`}, nil},
}
name := func(t testcase) string {
return fmt.Sprintf("%s.%s%v", t.suffix, t.method, t.args)
}
ipcServer := &server{
ctx: testContext(),
disp: testDisp{newEchoInvoker},
stats: newIPCStats(""),
}
for _, test := range tests {
clientFlow, serverFlow := newTestFlows()
client := newFlowClient(testContext(), []string{"p0"}, clientFlow, nil, nil)
server := newFlowServer(serverFlow, ipcServer)
err := client.start(test.suffix, test.method, test.args, 0, nil)
if err != nil {
t.Errorf("%s client.start unexpected error: %v", name(test), err)
}
if err := server.serve(); !verror.Equal(err, test.err) {
t.Errorf("%s server.server returned %v want %v", name(test), err, test.err)
}
results := makeResultPtrs(test.expect)
if err := client.Finish(results...); !verror.Equal(err, test.err) {
t.Errorf(`%s client.Finish got error "%v", want "%v"`, name(test), err, test.err)
}
checkResultPtrs(t, name(test), results, test.expect)
if clientFlow.numCloseCalls != 1 {
t.Errorf("%s got %d client close calls, want 1", name(test), clientFlow.numCloseCalls)
}
if serverFlow.numCloseCalls != 1 {
t.Errorf("%s got %d server close calls, want 1", name(test), serverFlow.numCloseCalls)
}
}
}