| // Copyright 2018 The Go 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 jsonrpc2_test |
| |
| import ( |
| "context" |
| "encoding/json" |
| "flag" |
| "fmt" |
| "net" |
| "path" |
| "reflect" |
| "testing" |
| |
| "golang.org/x/tools/internal/event/export/eventtest" |
| "golang.org/x/tools/internal/jsonrpc2" |
| "golang.org/x/tools/internal/stack/stacktest" |
| ) |
| |
| var logRPC = flag.Bool("logrpc", false, "Enable jsonrpc2 communication logging") |
| |
| type callTest struct { |
| method string |
| params interface{} |
| expect interface{} |
| } |
| |
| var callTests = []callTest{ |
| {"no_args", nil, true}, |
| {"one_string", "fish", "got:fish"}, |
| {"one_number", 10, "got:10"}, |
| {"join", []string{"a", "b", "c"}, "a/b/c"}, |
| //TODO: expand the test cases |
| } |
| |
| func (test *callTest) newResults() interface{} { |
| switch e := test.expect.(type) { |
| case []interface{}: |
| var r []interface{} |
| for _, v := range e { |
| r = append(r, reflect.New(reflect.TypeOf(v)).Interface()) |
| } |
| return r |
| case nil: |
| return nil |
| default: |
| return reflect.New(reflect.TypeOf(test.expect)).Interface() |
| } |
| } |
| |
| func (test *callTest) verifyResults(t *testing.T, results interface{}) { |
| if results == nil { |
| return |
| } |
| val := reflect.Indirect(reflect.ValueOf(results)).Interface() |
| if !reflect.DeepEqual(val, test.expect) { |
| t.Errorf("%v:Results are incorrect, got %+v expect %+v", test.method, val, test.expect) |
| } |
| } |
| |
| func TestCall(t *testing.T) { |
| stacktest.NoLeak(t) |
| ctx := eventtest.NewContext(context.Background(), t) |
| for _, headers := range []bool{false, true} { |
| name := "Plain" |
| if headers { |
| name = "Headers" |
| } |
| t.Run(name, func(t *testing.T) { |
| ctx := eventtest.NewContext(ctx, t) |
| a, b, done := prepare(ctx, t, headers) |
| defer done() |
| for _, test := range callTests { |
| t.Run(test.method, func(t *testing.T) { |
| ctx := eventtest.NewContext(ctx, t) |
| results := test.newResults() |
| if _, err := a.Call(ctx, test.method, test.params, results); err != nil { |
| t.Fatalf("%v:Call failed: %v", test.method, err) |
| } |
| test.verifyResults(t, results) |
| if _, err := b.Call(ctx, test.method, test.params, results); err != nil { |
| t.Fatalf("%v:Call failed: %v", test.method, err) |
| } |
| test.verifyResults(t, results) |
| }) |
| } |
| }) |
| } |
| } |
| |
| func prepare(ctx context.Context, t *testing.T, withHeaders bool) (jsonrpc2.Conn, jsonrpc2.Conn, func()) { |
| // make a wait group that can be used to wait for the system to shut down |
| aPipe, bPipe := net.Pipe() |
| a := run(ctx, withHeaders, aPipe) |
| b := run(ctx, withHeaders, bPipe) |
| return a, b, func() { |
| a.Close() |
| b.Close() |
| <-a.Done() |
| <-b.Done() |
| } |
| } |
| |
| func run(ctx context.Context, withHeaders bool, nc net.Conn) jsonrpc2.Conn { |
| var stream jsonrpc2.Stream |
| if withHeaders { |
| stream = jsonrpc2.NewHeaderStream(nc) |
| } else { |
| stream = jsonrpc2.NewRawStream(nc) |
| } |
| conn := jsonrpc2.NewConn(stream) |
| conn.Go(ctx, testHandler(*logRPC)) |
| return conn |
| } |
| |
| func testHandler(log bool) jsonrpc2.Handler { |
| return func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error { |
| switch req.Method() { |
| case "no_args": |
| if len(req.Params()) > 0 { |
| return reply(ctx, nil, fmt.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams)) |
| } |
| return reply(ctx, true, nil) |
| case "one_string": |
| var v string |
| if err := json.Unmarshal(req.Params(), &v); err != nil { |
| return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err)) |
| } |
| return reply(ctx, "got:"+v, nil) |
| case "one_number": |
| var v int |
| if err := json.Unmarshal(req.Params(), &v); err != nil { |
| return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err)) |
| } |
| return reply(ctx, fmt.Sprintf("got:%d", v), nil) |
| case "join": |
| var v []string |
| if err := json.Unmarshal(req.Params(), &v); err != nil { |
| return reply(ctx, nil, fmt.Errorf("%w: %s", jsonrpc2.ErrParse, err)) |
| } |
| return reply(ctx, path.Join(v...), nil) |
| default: |
| return jsonrpc2.MethodNotFound(ctx, reply, req) |
| } |
| } |
| } |