| // 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 main_test |
| |
| // This test assumes the vdl packages under v.io/x/ref/lib/vdl/testdata have been |
| // compiled using the vdl binary, and runs end-to-end rpc tests against the |
| // generated output. It's meant as a final sanity check of the vdl compiler; by |
| // using the compiled results we're behaving as an end-user would behave. |
| |
| import ( |
| "errors" |
| "math" |
| "reflect" |
| "testing" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "v.io/v23/rpc" |
| "v.io/v23/vdl" |
| "v.io/x/ref/lib/vdl/testdata/arith" |
| "v.io/x/ref/lib/vdl/testdata/base" |
| "v.io/x/ref/test" |
| |
| _ "v.io/x/ref/runtime/factories/generic" |
| ) |
| |
| var generatedError = errors.New("generated error") |
| |
| // serverArith implements the arith.Arith interface. |
| type serverArith struct{} |
| |
| func (*serverArith) Add(_ *context.T, _ rpc.ServerCall, A, B int32) (int32, error) { |
| return A + B, nil |
| } |
| |
| func (*serverArith) DivMod(_ *context.T, _ rpc.ServerCall, A, B int32) (int32, int32, error) { |
| return A / B, A % B, nil |
| } |
| |
| func (*serverArith) Sub(_ *context.T, _ rpc.ServerCall, args base.Args) (int32, error) { |
| return args.A - args.B, nil |
| } |
| |
| func (*serverArith) Mul(_ *context.T, _ rpc.ServerCall, nestedArgs base.NestedArgs) (int32, error) { |
| return nestedArgs.Args.A * nestedArgs.Args.B, nil |
| } |
| |
| func (*serverArith) Count(_ *context.T, call arith.ArithCountServerCall, start int32) error { |
| const kNum = 1000 |
| for i := int32(0); i < kNum; i++ { |
| if err := call.SendStream().Send(start + i); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func (*serverArith) StreamingAdd(_ *context.T, call arith.ArithStreamingAddServerCall) (int32, error) { |
| var total int32 |
| for call.RecvStream().Advance() { |
| value := call.RecvStream().Value() |
| total += value |
| call.SendStream().Send(total) |
| } |
| return total, call.RecvStream().Err() |
| } |
| |
| func (*serverArith) GenError(_ *context.T, _ rpc.ServerCall) error { |
| return generatedError |
| } |
| |
| func (*serverArith) QuoteAny(_ *context.T, _ rpc.ServerCall, any *vdl.Value) (*vdl.Value, error) { |
| return vdl.StringValue(any.String()), nil |
| } |
| |
| type serverCalculator struct { |
| serverArith |
| } |
| |
| func (*serverCalculator) Sine(_ *context.T, _ rpc.ServerCall, angle float64) (float64, error) { |
| return math.Sin(angle), nil |
| } |
| |
| func (*serverCalculator) Cosine(_ *context.T, _ rpc.ServerCall, angle float64) (float64, error) { |
| return math.Cos(angle), nil |
| } |
| |
| func (*serverCalculator) Exp(_ *context.T, _ rpc.ServerCall, x float64) (float64, error) { |
| return math.Exp(x), nil |
| } |
| |
| func (*serverCalculator) On(_ *context.T, _ rpc.ServerCall) error { |
| return nil |
| } |
| |
| func (*serverCalculator) Off(_ *context.T, _ rpc.ServerCall) error { |
| return nil |
| } |
| |
| func TestCalculator(t *testing.T) { |
| ctx, shutdown := test.V23Init() |
| defer shutdown() |
| |
| _, server, err := v23.WithNewServer(ctx, "", arith.CalculatorServer(&serverCalculator{}), nil) |
| if err != nil { |
| t.Fatal(err) |
| } |
| root := server.Status().Endpoints[0].Name() |
| // Synchronous calls |
| calculator := arith.CalculatorClient(root) |
| sine, err := calculator.Sine(ctx, 0) |
| if err != nil { |
| t.Errorf("Sine: got %q but expected no error", err) |
| } |
| if sine != 0 { |
| t.Errorf("Sine: expected 0 got %f", sine) |
| } |
| cosine, err := calculator.Cosine(ctx, 0) |
| if err != nil { |
| t.Errorf("Cosine: got %q but expected no error", err) |
| } |
| if cosine != 1 { |
| t.Errorf("Cosine: expected 1 got %f", cosine) |
| } |
| |
| ar := arith.ArithClient(root) |
| sum, err := ar.Add(ctx, 7, 8) |
| if err != nil { |
| t.Errorf("Add: got %q but expected no error", err) |
| } |
| if sum != 15 { |
| t.Errorf("Add: expected 15 got %d", sum) |
| } |
| ar = calculator |
| sum, err = ar.Add(ctx, 7, 8) |
| if err != nil { |
| t.Errorf("Add: got %q but expected no error", err) |
| } |
| if sum != 15 { |
| t.Errorf("Add: expected 15 got %d", sum) |
| } |
| |
| trig := arith.TrigonometryClient(root) |
| cosine, err = trig.Cosine(ctx, 0) |
| if err != nil { |
| t.Errorf("Cosine: got %q but expected no error", err) |
| } |
| if cosine != 1 { |
| t.Errorf("Cosine: expected 1 got %f", cosine) |
| } |
| |
| // Test auto-generated methods. |
| serverStub := arith.CalculatorServer(&serverCalculator{}) |
| expectDesc(t, serverStub.Describe__(), []rpc.InterfaceDesc{ |
| { |
| Name: "Calculator", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| Embeds: []rpc.EmbedDesc{ |
| { |
| Name: "Arith", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| }, |
| { |
| Name: "AdvancedMath", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| }, |
| }, |
| Methods: []rpc.MethodDesc{ |
| {Name: "On"}, |
| {Name: "Off", Tags: []*vdl.Value{vdl.StringValue("offtag")}}, |
| }, |
| }, |
| { |
| Name: "Arith", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| Methods: []rpc.MethodDesc{ |
| { |
| Name: "Add", |
| InArgs: []rpc.ArgDesc{{Name: "a"}, {Name: "b"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| { |
| Name: "DivMod", |
| InArgs: []rpc.ArgDesc{{Name: "a"}, {Name: "b"}}, |
| OutArgs: []rpc.ArgDesc{{Name: "quot"}, {Name: "rem"}}, |
| }, |
| { |
| Name: "Sub", |
| InArgs: []rpc.ArgDesc{{Name: "args"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| { |
| Name: "Mul", |
| InArgs: []rpc.ArgDesc{{Name: "nested"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| { |
| Name: "GenError", |
| Tags: []*vdl.Value{vdl.StringValue("foo"), vdl.StringValue("barz"), vdl.StringValue("hello"), vdl.Int32Value(129), vdl.Uint64Value(0x24)}, |
| }, |
| { |
| Name: "Count", |
| InArgs: []rpc.ArgDesc{{Name: "start"}}, |
| }, |
| { |
| Name: "StreamingAdd", |
| OutArgs: []rpc.ArgDesc{{Name: "total"}}, |
| }, |
| { |
| Name: "QuoteAny", |
| InArgs: []rpc.ArgDesc{{Name: "a"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| }, |
| }, |
| { |
| Name: "AdvancedMath", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| Embeds: []rpc.EmbedDesc{ |
| { |
| Name: "Trigonometry", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| }, |
| { |
| Name: "Exp", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith/exp", |
| }}, |
| }, |
| { |
| Name: "Trigonometry", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| Doc: "// Trigonometry is an interface that specifies a couple trigonometric functions.", |
| Methods: []rpc.MethodDesc{ |
| { |
| Name: "Sine", |
| InArgs: []rpc.ArgDesc{ |
| {"angle", ``}, // float64 |
| }, |
| OutArgs: []rpc.ArgDesc{ |
| {"", ``}, // float64 |
| }, |
| }, |
| { |
| Name: "Cosine", |
| InArgs: []rpc.ArgDesc{ |
| {"angle", ``}, // float64 |
| }, |
| OutArgs: []rpc.ArgDesc{ |
| {"", ``}, // float64 |
| }, |
| }, |
| }, |
| }, |
| { |
| Name: "Exp", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith/exp", |
| Methods: []rpc.MethodDesc{ |
| { |
| Name: "Exp", |
| InArgs: []rpc.ArgDesc{ |
| {"x", ``}, // float64 |
| }, |
| OutArgs: []rpc.ArgDesc{ |
| {"", ``}, // float64 |
| }, |
| }, |
| }, |
| }, |
| }) |
| } |
| |
| func TestArith(t *testing.T) { |
| ctx, shutdown := test.V23Init() |
| defer shutdown() |
| |
| // TODO(bprosnitz) Split this test up -- it is quite long and hard to debug. |
| |
| // We try a few types of dispatchers on the server side, to verify that |
| // anything dispatching to Arith or an interface embedding Arith (like |
| // Calculator) works for a client looking to talk to an Arith service. |
| objects := []interface{}{ |
| arith.ArithServer(&serverArith{}), |
| arith.ArithServer(&serverCalculator{}), |
| arith.CalculatorServer(&serverCalculator{}), |
| } |
| |
| for i, obj := range objects { |
| _, server, err := v23.WithNewServer(ctx, "", obj, nil) |
| if err != nil { |
| t.Fatalf("%d: %v", i, err) |
| } |
| root := server.Status().Endpoints[0].Name() |
| |
| // Synchronous calls |
| ar := arith.ArithClient(root) |
| sum, err := ar.Add(ctx, 7, 8) |
| if err != nil { |
| t.Errorf("Add: got %q but expected no error", err) |
| } |
| if sum != 15 { |
| t.Errorf("Add: expected 15 got %d", sum) |
| } |
| q, r, err := ar.DivMod(ctx, 7, 3) |
| if err != nil { |
| t.Errorf("DivMod: got %q but expected no error", err) |
| } |
| if q != 2 || r != 1 { |
| t.Errorf("DivMod: expected (2,1) got (%d,%d)", q, r) |
| } |
| diff, err := ar.Sub(ctx, base.Args{A: 7, B: 8}) |
| if err != nil { |
| t.Errorf("Sub: got %q but expected no error", err) |
| } |
| if diff != -1 { |
| t.Errorf("Sub: got %d, expected -1", diff) |
| } |
| prod, err := ar.Mul(ctx, base.NestedArgs{Args: base.Args{A: 7, B: 8}}) |
| if err != nil { |
| t.Errorf("Mul: got %q, but expected no error", err) |
| } |
| if prod != 56 { |
| t.Errorf("Sub: got %d, expected 56", prod) |
| } |
| stream, err := ar.Count(ctx, 35) |
| if err != nil { |
| t.Fatalf("error while executing Count %v", err) |
| } |
| |
| countIterator := stream.RecvStream() |
| for i := int32(0); i < 1000; i++ { |
| if !countIterator.Advance() { |
| t.Errorf("Error getting value %v", countIterator.Err()) |
| } |
| val := countIterator.Value() |
| if val != 35+i { |
| t.Errorf("Expected value %d, got %d", 35+i, val) |
| } |
| } |
| if countIterator.Advance() || countIterator.Err() != nil { |
| t.Errorf("Reply stream should have been closed %v", countIterator.Err()) |
| } |
| |
| if err := stream.Finish(); err != nil { |
| t.Errorf("Count failed with %v", err) |
| } |
| |
| addStream, err := ar.StreamingAdd(ctx) |
| |
| go func() { |
| sender := addStream.SendStream() |
| for i := int32(0); i < 100; i++ { |
| if err := sender.Send(i); err != nil { |
| t.Errorf("Send error %v", err) |
| } |
| } |
| if err := sender.Close(); err != nil { |
| t.Errorf("Close error %v", err) |
| } |
| }() |
| |
| var expectedSum int32 |
| rStream := addStream.RecvStream() |
| for i := int32(0); i < 100; i++ { |
| expectedSum += i |
| if !rStream.Advance() { |
| t.Errorf("Error getting value %v", rStream.Err()) |
| } |
| value := rStream.Value() |
| if value != expectedSum { |
| t.Errorf("Got %d but expected %d", value, expectedSum) |
| } |
| } |
| |
| if rStream.Advance() || rStream.Err() != nil { |
| t.Errorf("Reply stream should have been closed %v", rStream.Err()) |
| } |
| |
| total, err := addStream.Finish() |
| |
| if err != nil { |
| t.Errorf("Count failed with %v", err) |
| } |
| |
| if total != expectedSum { |
| t.Errorf("Got %d but expexted %d", total, expectedSum) |
| } |
| |
| if err := ar.GenError(ctx); err == nil { |
| t.Errorf("GenError: got %v but expected %v", err, generatedError) |
| } |
| |
| // Server-side stubs |
| |
| serverStub := arith.ArithServer(&serverArith{}) |
| expectDesc(t, serverStub.Describe__(), []rpc.InterfaceDesc{ |
| { |
| Name: "Arith", |
| PkgPath: "v.io/x/ref/lib/vdl/testdata/arith", |
| Methods: []rpc.MethodDesc{ |
| { |
| Name: "Add", |
| InArgs: []rpc.ArgDesc{{Name: "a"}, {Name: "b"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| { |
| Name: "DivMod", |
| InArgs: []rpc.ArgDesc{{Name: "a"}, {Name: "b"}}, |
| OutArgs: []rpc.ArgDesc{{Name: "quot"}, {Name: "rem"}}, |
| }, |
| { |
| Name: "Sub", |
| InArgs: []rpc.ArgDesc{{Name: "args"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| { |
| Name: "Mul", |
| InArgs: []rpc.ArgDesc{{Name: "nested"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| { |
| Name: "GenError", |
| Tags: []*vdl.Value{vdl.StringValue("foo"), vdl.StringValue("barz"), vdl.StringValue("hello"), vdl.Int32Value(129), vdl.Uint64Value(0x24)}, |
| }, |
| { |
| Name: "Count", |
| InArgs: []rpc.ArgDesc{{Name: "start"}}, |
| }, |
| { |
| Name: "StreamingAdd", |
| OutArgs: []rpc.ArgDesc{{Name: "total"}}, |
| }, |
| { |
| Name: "QuoteAny", |
| InArgs: []rpc.ArgDesc{{Name: "a"}}, |
| OutArgs: []rpc.ArgDesc{{}}, |
| }, |
| }, |
| }, |
| }) |
| } |
| } |
| |
| func expectDesc(t *testing.T, got, want []rpc.InterfaceDesc) { |
| stripDesc(got) |
| stripDesc(want) |
| if !reflect.DeepEqual(got, want) { |
| t.Errorf("Describe__ got %#v, want %#v", got, want) |
| } |
| } |
| |
| func stripDesc(desc []rpc.InterfaceDesc) { |
| // Don't bother testing the documentation, to avoid spurious changes. |
| for i := range desc { |
| desc[i].Doc = "" |
| for j := range desc[i].Embeds { |
| desc[i].Embeds[j].Doc = "" |
| } |
| for j := range desc[i].Methods { |
| desc[i].Methods[j].Doc = "" |
| for k := range desc[i].Methods[j].InArgs { |
| desc[i].Methods[j].InArgs[k].Doc = "" |
| } |
| for k := range desc[i].Methods[j].OutArgs { |
| desc[i].Methods[j].OutArgs[k].Doc = "" |
| } |
| desc[i].Methods[j].InStream.Doc = "" |
| desc[i].Methods[j].OutStream.Doc = "" |
| } |
| } |
| } |