blob: 091e606e258eaa7d426665929852df3bb2bfd737 [file] [log] [blame]
package impl_test
import (
"bytes"
"sort"
"strings"
"testing"
"veyron/lib/cmdline"
"veyron/tools/vrpc/impl"
"veyron/tools/vrpc/test_base"
"veyron2"
"veyron2/ipc"
"veyron2/naming"
"veyron2/rt"
"veyron2/vlog"
)
type server struct{}
// TypeTester interface implementation
func (*server) EchoBool(call ipc.ServerContext, i1 bool) (bool, error) {
vlog.VI(2).Info("EchoBool(%v) was called.", i1)
return i1, nil
}
func (*server) EchoFloat32(call ipc.ServerContext, i1 float32) (float32, error) {
vlog.VI(2).Info("EchoFloat32(%u) was called.", i1)
return i1, nil
}
func (*server) EchoFloat64(call ipc.ServerContext, i1 float64) (float64, error) {
vlog.VI(2).Info("EchoFloat64(%u) was called.", i1)
return i1, nil
}
func (*server) EchoInt32(call ipc.ServerContext, i1 int32) (int32, error) {
vlog.VI(2).Info("EchoInt32(%v) was called.", i1)
return i1, nil
}
func (*server) EchoInt64(call ipc.ServerContext, i1 int64) (int64, error) {
vlog.VI(2).Info("EchoInt64(%v) was called.", i1)
return i1, nil
}
func (*server) EchoString(call ipc.ServerContext, i1 string) (string, error) {
vlog.VI(2).Info("EchoString(%v) was called.", i1)
return i1, nil
}
func (*server) EchoByte(call ipc.ServerContext, i1 byte) (byte, error) {
vlog.VI(2).Info("EchoByte(%v) was called.", i1)
return i1, nil
}
func (*server) EchoUInt32(call ipc.ServerContext, i1 uint32) (uint32, error) {
vlog.VI(2).Info("EchoUInt32(%u) was called.", i1)
return i1, nil
}
func (*server) EchoUInt64(call ipc.ServerContext, i1 uint64) (uint64, error) {
vlog.VI(2).Info("EchoUInt64(%u) was called.", i1)
return i1, nil
}
func (*server) InputArray(call ipc.ServerContext, i1 [2]uint8) error {
vlog.VI(2).Info("CInputArray(%v) was called.", i1)
return nil
}
func (*server) OutputArray(call ipc.ServerContext) ([2]uint8, error) {
vlog.VI(2).Info("COutputArray() was called.")
return [2]uint8{1, 2}, nil
}
func (*server) InputMap(call ipc.ServerContext, i1 map[uint8]uint8) error {
vlog.VI(2).Info("CInputMap(%v) was called.", i1)
return nil
}
func (*server) OutputMap(call ipc.ServerContext) (map[uint8]uint8, error) {
vlog.VI(2).Info("COutputMap() was called.")
return map[uint8]uint8{1: 2}, nil
}
func (*server) InputSlice(call ipc.ServerContext, i1 []uint8) error {
vlog.VI(2).Info("CInputSlice(%v) was called.", i1)
return nil
}
func (*server) OutputSlice(call ipc.ServerContext) ([]uint8, error) {
vlog.VI(2).Info("COutputSlice() was called.")
return []uint8{1, 2}, nil
}
func (*server) InputStruct(call ipc.ServerContext, i1 test_base.Struct) error {
vlog.VI(2).Info("CInputStruct(%v) was called.", i1)
return nil
}
func (*server) OutputStruct(call ipc.ServerContext) (test_base.Struct, error) {
vlog.VI(2).Info("COutputStruct() was called.")
return test_base.Struct{X: 1, Y: 2}, nil
}
func (*server) NoArguments(call ipc.ServerContext) error {
vlog.VI(2).Info("NoArguments() was called.")
return nil
}
func (*server) MultipleArguments(call ipc.ServerContext, i1, i2 int32) (int32, int32, error) {
vlog.VI(2).Info("MultipleArguments(%v,%v) was called.", i1, i2)
return i1, i2, nil
}
func (*server) StreamingOutput(call ipc.ServerContext, nStream int32, item bool, reply test_base.TypeTesterServiceStreamingOutputStream) error {
vlog.VI(2).Info("StreamingOutput(%v,%v) was called.", nStream, item)
sender := reply.SendStream()
for i := int32(0); i < nStream; i++ {
sender.Send(item)
}
return nil
}
func startServer(t *testing.T, r veyron2.Runtime) (ipc.Server, naming.Endpoint, error) {
dispatcher := ipc.LeafDispatcher(test_base.NewServerTypeTester(&server{}), nil)
server, err := r.NewServer()
if err != nil {
t.Errorf("NewServer failed: %v", err)
return nil, nil, err
}
endpoint, err := server.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Errorf("Listen failed: %v", err)
return nil, nil, err
}
if err := server.Serve("", dispatcher); err != nil {
t.Errorf("Serve failed: %v", err)
return nil, nil, err
}
return server, endpoint, nil
}
func stopServer(t *testing.T, server ipc.Server) {
if err := server.Stop(); err != nil {
t.Errorf("server.Stop failed: %v", err)
}
}
func testInvocation(t *testing.T, buffer *bytes.Buffer, cmd *cmdline.Command, args []string, expected string) {
buffer.Reset()
if err := cmd.Execute(args); err != nil {
t.Errorf("%v", err)
return
}
if output := strings.Trim(buffer.String(), "\n"); output != expected {
t.Errorf("Incorrect invoke output: expected %s, got %s", expected, output)
return
}
}
func testError(t *testing.T, cmd *cmdline.Command, args []string, expected string) {
if err := cmd.Execute(args); err == nil || !strings.Contains(err.Error(), expected) {
t.Errorf("Expected error: ...%v..., got: %v", expected, err)
}
}
func TestVRPC(t *testing.T) {
runtime := rt.Init()
// Skip defer runtime.Cleanup() to avoid messing up other tests in the
// same process.
server, endpoint, err := startServer(t, runtime)
if err != nil {
return
}
defer stopServer(t, server)
// Setup the command-line.
cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
name := naming.JoinAddressName(endpoint.String(), "//")
// Test the 'describe' command.
if err := cmd.Execute([]string{"describe", name}); err != nil {
t.Errorf("%v", err)
return
}
expectedSignature := []string{
"func EchoBool(I1 bool) (O1 bool, E error)",
"func EchoFloat32(I1 float32) (O1 float32, E error)",
"func EchoFloat64(I1 float64) (O1 float64, E error)",
"func EchoInt32(I1 int32) (O1 int32, E error)",
"func EchoInt64(I1 int64) (O1 int64, E error)",
"func EchoString(I1 string) (O1 string, E error)",
"func EchoByte(I1 byte) (O1 byte, E error)",
"func EchoUInt32(I1 uint32) (O1 uint32, E error)",
"func EchoUInt64(I1 uint64) (O1 uint64, E error)",
"func InputArray(I1 [2]byte) (E error)",
"func InputMap(I1 map[byte]byte) (E error)",
"func InputSlice(I1 []byte) (E error)",
"func InputStruct(I1 struct{X int32, Y int32}) (E error)",
"func OutputArray() (O1 [2]byte, E error)",
"func OutputMap() (O1 map[byte]byte, E error)",
"func OutputSlice() (O1 []byte, E error)",
"func OutputStruct() (O1 struct{X int32, Y int32}, E error)",
"func NoArguments() (error)",
"func MultipleArguments(I1 int32, I2 int32) (O1 int32, O2 int32, E error)",
"func StreamingOutput(NumStreamItems int32, StreamItem bool) stream<_, bool> (error)",
}
signature := make([]string, 0, len(expectedSignature))
line, err := stdout.ReadBytes('\n')
for err == nil {
signature = append(signature, strings.Trim(string(line), "\n"))
line, err = stdout.ReadBytes('\n')
}
sort.Strings(signature)
sort.Strings(expectedSignature)
if len(signature) != len(expectedSignature) {
t.Fatalf("signature lengths don't match %v and %v.", len(signature), len(expectedSignature))
}
for i, expectedSig := range expectedSignature {
if expectedSig != signature[i] {
t.Errorf("signature line doesn't match: %v and %v\n", expectedSig, signature[i])
}
}
// Test the 'invoke' command.
tests := [][]string{
[]string{"EchoBool", "EchoBool(true) = [true, <nil>]", "[\"bool\",true]"},
[]string{"EchoFloat32", "EchoFloat32(3.2) = [3.2, <nil>]", "[\"float32\",3.2]"},
[]string{"EchoFloat64", "EchoFloat64(6.4) = [6.4, <nil>]", "[\"float64\",6.4]"},
[]string{"EchoInt32", "EchoInt32(-32) = [-32, <nil>]", "[\"int32\",-32]"},
[]string{"EchoInt64", "EchoInt64(-64) = [-64, <nil>]", "[\"int64\",-64]"},
[]string{"EchoString", "EchoString(Hello World!) = [Hello World!, <nil>]", "[\"string\",\"Hello World!\"]"},
[]string{"EchoByte", "EchoByte(8) = [8, <nil>]", "[\"byte\",8]"},
[]string{"EchoUInt32", "EchoUInt32(32) = [32, <nil>]", "[\"uint32\",32]"},
[]string{"EchoUInt64", "EchoUInt64(64) = [64, <nil>]", "[\"uint64\",64]"},
// TODO(jsimsa): The InputArray currently triggers an error in the
// vom decoder. Benj is looking into this.
//
// []string{"InputArray", "InputArray([1 2]) = []", "[\"[2]uint\",[1,2]]"},
[]string{"InputMap", "InputMap(map[1:2]) = [<nil>]", "[\"map[uint]uint\",{\"1\":\"2\"}]"},
// TODO(jsimsa): The InputSlice currently triggers an error in the
// vom decoder. Benj is looking into this.
//
// []string{"InputSlice", "InputSlice([1 2]) = []", "[\"[]uint\",[1,2]]"},
[]string{"InputStruct", "InputStruct({1 2}) = [<nil>]",
"[\"type\",\"veyron2/vrpc/test_base.Struct struct{X int32;Y int32}\"] [\"Struct\",{\"X\":1,\"Y\":2}]"},
// TODO(jsimsa): The OutputArray currently triggers an error in the
// vom decoder. Benj is looking into this.
//
// []string{"OutputArray", "OutputArray() = [1 2]"}
[]string{"OutputMap", "OutputMap() = [map[1:2], <nil>]"},
[]string{"OutputSlice", "OutputSlice() = [[1 2], <nil>]"},
[]string{"OutputStruct", "OutputStruct() = [{1 2}, <nil>]"},
[]string{"NoArguments", "NoArguments() = [<nil>]"},
[]string{"MultipleArguments", "MultipleArguments(1, 2) = [1, 2, <nil>]", "[\"uint32\",1]", "[\"uint32\",2]"},
[]string{"StreamingOutput", "StreamingOutput(3, true) = <<\n0: true\n1: true\n2: true\n>> [<nil>]", "[\"int8\",3]", "[\"bool\",true ]"},
[]string{"StreamingOutput", "StreamingOutput(0, true) = [<nil>]", "[\"int8\",0]", "[\"bool\",true ]"},
}
for _, test := range tests {
testInvocation(t, &stdout, cmd, append([]string{"invoke", name, test[0]}, test[2:]...), test[1])
}
testErrors := [][]string{
[]string{"EchoBool", "usage error"},
[]string{"DoesNotExit", "invoke: method DoesNotExit not found"},
}
for _, test := range testErrors {
testError(t, cmd, append([]string{"invoke", name, test[0]}, test[2:]...), test[1])
}
}