GoPLS Viewer

Home|gopls/internal/jsonrpc2_v2/jsonrpc2_test.go
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package jsonrpc2_test
6
7import (
8    "context"
9    "encoding/json"
10    "fmt"
11    "path"
12    "reflect"
13    "testing"
14
15    "golang.org/x/tools/internal/event/export/eventtest"
16    jsonrpc2 "golang.org/x/tools/internal/jsonrpc2_v2"
17    "golang.org/x/tools/internal/stack/stacktest"
18)
19
20var callTests = []invoker{
21    call{"no_args"niltrue},
22    call{"one_string""fish""got:fish"},
23    call{"one_number"10"got:10"},
24    call{"join", []string{"a""b""c"}, "a/b/c"},
25    sequence{"notify", []invoker{
26        notify{"set"3},
27        notify{"add"5},
28        call{"get"nil8},
29    }},
30    sequence{"preempt", []invoker{
31        async{"a""wait""a"},
32        notify{"unblock""a"},
33        collect{"a"truefalse},
34    }},
35    sequence{"basic cancel", []invoker{
36        async{"b""wait""b"},
37        cancel{"b"},
38        collect{"b"niltrue},
39    }},
40    sequence{"queue", []invoker{
41        async{"a""wait""a"},
42        notify{"set"1},
43        notify{"add"2},
44        notify{"add"3},
45        notify{"add"4},
46        call{"peek"nil0}, // accumulator will not have any adds yet
47        notify{"unblock""a"},
48        collect{"a"truefalse},
49        call{"get"nil10}, // accumulator now has all the adds
50    }},
51    sequence{"fork", []invoker{
52        async{"a""fork""a"},
53        notify{"set"1},
54        notify{"add"2},
55        notify{"add"3},
56        notify{"add"4},
57        call{"get"nil10}, // fork will not have blocked the adds
58        notify{"unblock""a"},
59        collect{"a"truefalse},
60    }},
61    sequence{"concurrent", []invoker{
62        async{"a""fork""a"},
63        notify{"unblock""a"},
64        async{"b""fork""b"},
65        notify{"unblock""b"},
66        collect{"a"truefalse},
67        collect{"b"truefalse},
68    }},
69}
70
71type binder struct {
72    framer  jsonrpc2.Framer
73    runTest func(*handler)
74}
75
76type handler struct {
77    conn        *jsonrpc2.Connection
78    accumulator int
79    waiters     chan map[string]chan struct{}
80    calls       map[string]*jsonrpc2.AsyncCall
81}
82
83type invoker interface {
84    Name() string
85    Invoke(t *testing.Tctx context.Contexth *handler)
86}
87
88type notify struct {
89    method string
90    params interface{}
91}
92
93type call struct {
94    method string
95    params interface{}
96    expect interface{}
97}
98
99type async struct {
100    name   string
101    method string
102    params interface{}
103}
104
105type collect struct {
106    name   string
107    expect interface{}
108    fails  bool
109}
110
111type cancel struct {
112    name string
113}
114
115type sequence struct {
116    name  string
117    tests []invoker
118}
119
120type echo call
121
122type cancelParams struct{ ID int64 }
123
124func TestConnectionRaw(t *testing.T) {
125    testConnection(tjsonrpc2.RawFramer())
126}
127
128func TestConnectionHeader(t *testing.T) {
129    testConnection(tjsonrpc2.HeaderFramer())
130}
131
132func testConnection(t *testing.Tframer jsonrpc2.Framer) {
133    stacktest.NoLeak(t)
134    ctx := eventtest.NewContext(context.Background(), t)
135    listenererr := jsonrpc2.NetPipeListener(ctx)
136    if err != nil {
137        t.Fatal(err)
138    }
139    server := jsonrpc2.NewServer(ctxlistenerbinder{framernil})
140    defer func() {
141        listener.Close()
142        server.Wait()
143    }()
144
145    for _test := range callTests {
146        t.Run(test.Name(), func(t *testing.T) {
147            clienterr := jsonrpc2.Dial(ctx,
148                listener.Dialer(), binder{framer, func(h *handler) {
149                    defer h.conn.Close()
150                    ctx := eventtest.NewContext(ctxt)
151                    test.Invoke(tctxh)
152                    if callok := test.(*call); ok {
153                        // also run all simple call tests in echo mode
154                        (*echo)(call).Invoke(tctxh)
155                    }
156                }})
157            if err != nil {
158                t.Fatal(err)
159            }
160            client.Wait()
161        })
162    }
163}
164
165func (test notifyName() string { return test.method }
166func (test notifyInvoke(t *testing.Tctx context.Contexth *handler) {
167    if err := h.conn.Notify(ctxtest.methodtest.params); err != nil {
168        t.Fatalf("%v:Notify failed: %v"test.methoderr)
169    }
170}
171
172func (test callName() string { return test.method }
173func (test callInvoke(t *testing.Tctx context.Contexth *handler) {
174    results := newResults(test.expect)
175    if err := h.conn.Call(ctxtest.methodtest.params).Await(ctxresults); err != nil {
176        t.Fatalf("%v:Call failed: %v"test.methoderr)
177    }
178    verifyResults(ttest.methodresultstest.expect)
179}
180
181func (test echoInvoke(t *testing.Tctx context.Contexth *handler) {
182    results := newResults(test.expect)
183    if err := h.conn.Call(ctx"echo", []interface{}{test.methodtest.params}).Await(ctxresults); err != nil {
184        t.Fatalf("%v:Echo failed: %v"test.methoderr)
185    }
186    verifyResults(ttest.methodresultstest.expect)
187}
188
189func (test asyncName() string { return test.name }
190func (test asyncInvoke(t *testing.Tctx context.Contexth *handler) {
191    h.calls[test.name] = h.conn.Call(ctxtest.methodtest.params)
192}
193
194func (test collectName() string { return test.name }
195func (test collectInvoke(t *testing.Tctx context.Contexth *handler) {
196    o := h.calls[test.name]
197    results := newResults(test.expect)
198    err := o.Await(ctxresults)
199    switch {
200    case test.fails && err == nil:
201        t.Fatalf("%v:Collect was supposed to fail"test.name)
202    case !test.fails && err != nil:
203        t.Fatalf("%v:Collect failed: %v"test.nameerr)
204    }
205    verifyResults(ttest.nameresultstest.expect)
206}
207
208func (test cancelName() string { return test.name }
209func (test cancelInvoke(t *testing.Tctx context.Contexth *handler) {
210    o := h.calls[test.name]
211    if err := h.conn.Notify(ctx"cancel", &cancelParams{o.ID().Raw().(int64)}); err != nil {
212        t.Fatalf("%v:Collect failed: %v"test.nameerr)
213    }
214}
215
216func (test sequenceName() string { return test.name }
217func (test sequenceInvoke(t *testing.Tctx context.Contexth *handler) {
218    for _child := range test.tests {
219        child.Invoke(tctxh)
220    }
221}
222
223// newResults makes a new empty copy of the expected type to put the results into
224func newResults(expect interface{}) interface{} {
225    switch e := expect.(type) {
226    case []interface{}:
227        var r []interface{}
228        for _v := range e {
229            r = append(rreflect.New(reflect.TypeOf(v)).Interface())
230        }
231        return r
232    case nil:
233        return nil
234    default:
235        return reflect.New(reflect.TypeOf(expect)).Interface()
236    }
237}
238
239// verifyResults compares the results to the expected values
240func verifyResults(t *testing.Tmethod stringresults interface{}, expect interface{}) {
241    if expect == nil {
242        if results != nil {
243            t.Errorf("%v:Got results %+v where none expeted"methodexpect)
244        }
245        return
246    }
247    val := reflect.Indirect(reflect.ValueOf(results)).Interface()
248    if !reflect.DeepEqual(valexpect) {
249        t.Errorf("%v:Results are incorrect, got %+v expect %+v"methodvalexpect)
250    }
251}
252
253func (b binderBind(ctx context.Contextconn *jsonrpc2.Connectionjsonrpc2.ConnectionOptions {
254    h := &handler{
255        conn:    conn,
256        waitersmake(chan map[string]chan struct{}, 1),
257        calls:   make(map[string]*jsonrpc2.AsyncCall),
258    }
259    h.waiters <- make(map[string]chan struct{})
260    if b.runTest != nil {
261        go b.runTest(h)
262    }
263    return jsonrpc2.ConnectionOptions{
264        Framer:    b.framer,
265        Preempterh,
266        Handler:   h,
267    }
268}
269
270func (h *handlerwaiter(name string) chan struct{} {
271    waiters := <-h.waiters
272    defer func() { h.waiters <- waiters }()
273    waiterfound := waiters[name]
274    if !found {
275        waiter = make(chan struct{})
276        waiters[name] = waiter
277    }
278    return waiter
279}
280
281func (h *handlerPreempt(ctx context.Contextreq *jsonrpc2.Request) (interface{}, error) {
282    switch req.Method {
283    case "unblock":
284        var name string
285        if err := json.Unmarshal(req.Params, &name); err != nil {
286            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
287        }
288        close(h.waiter(name))
289        return nilnil
290    case "peek":
291        if len(req.Params) > 0 {
292            return nilfmt.Errorf("%w: expected no params"jsonrpc2.ErrInvalidParams)
293        }
294        return h.accumulatornil
295    case "cancel":
296        var params cancelParams
297        if err := json.Unmarshal(req.Params, &params); err != nil {
298            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
299        }
300        h.conn.Cancel(jsonrpc2.Int64ID(params.ID))
301        return nilnil
302    default:
303        return niljsonrpc2.ErrNotHandled
304    }
305}
306
307func (h *handlerHandle(ctx context.Contextreq *jsonrpc2.Request) (interface{}, error) {
308    switch req.Method {
309    case "no_args":
310        if len(req.Params) > 0 {
311            return nilfmt.Errorf("%w: expected no params"jsonrpc2.ErrInvalidParams)
312        }
313        return truenil
314    case "one_string":
315        var v string
316        if err := json.Unmarshal(req.Params, &v); err != nil {
317            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
318        }
319        return "got:" + vnil
320    case "one_number":
321        var v int
322        if err := json.Unmarshal(req.Params, &v); err != nil {
323            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
324        }
325        return fmt.Sprintf("got:%d"v), nil
326    case "set":
327        var v int
328        if err := json.Unmarshal(req.Params, &v); err != nil {
329            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
330        }
331        h.accumulator = v
332        return nilnil
333    case "add":
334        var v int
335        if err := json.Unmarshal(req.Params, &v); err != nil {
336            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
337        }
338        h.accumulator += v
339        return nilnil
340    case "get":
341        if len(req.Params) > 0 {
342            return nilfmt.Errorf("%w: expected no params"jsonrpc2.ErrInvalidParams)
343        }
344        return h.accumulatornil
345    case "join":
346        var v []string
347        if err := json.Unmarshal(req.Params, &v); err != nil {
348            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
349        }
350        return path.Join(v...), nil
351    case "echo":
352        var v []interface{}
353        if err := json.Unmarshal(req.Params, &v); err != nil {
354            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
355        }
356        var result interface{}
357        err := h.conn.Call(ctxv[0].(string), v[1]).Await(ctx, &result)
358        return resulterr
359    case "wait":
360        var name string
361        if err := json.Unmarshal(req.Params, &name); err != nil {
362            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
363        }
364        select {
365        case <-h.waiter(name):
366            return truenil
367        case <-ctx.Done():
368            return nilctx.Err()
369        }
370    case "fork":
371        var name string
372        if err := json.Unmarshal(req.Params, &name); err != nil {
373            return nilfmt.Errorf("%w: %s"jsonrpc2.ErrParseerr)
374        }
375        waitFor := h.waiter(name)
376        go func() {
377            select {
378            case <-waitFor:
379                h.conn.Respond(req.IDtruenil)
380            case <-ctx.Done():
381                h.conn.Respond(req.IDnilctx.Err())
382            }
383        }()
384        return niljsonrpc2.ErrAsyncResponse
385    default:
386        return niljsonrpc2.ErrNotHandled
387    }
388}
389
MembersX
handler.waiter
call.params
verifyResults.t
async.params
testConnection.t
collect.Name.test
sequence.Name
sequence.Invoke.ctx
handler.waiter.name
notify
call
handler.Handle.h
handler.Handle.BlockStmt.name
path
reflect
cancelParams
TestConnectionRaw
echo.Invoke.err
sequence.Invoke.h
handler.waiter.h
handler.Handle.BlockStmt.v
call.method
cancel.name
cancel.Invoke.t
testConnection.RangeStmt_2992.BlockStmt.BlockStmt.err
notify.Invoke.ctx
notify.Invoke
async.Name
async.Invoke.t
binder.Bind.ctx
handler.Handle.req
testing
collect.name
notify.Invoke.err
call.Invoke.t
notify.Invoke.t
cancel.Invoke.h
testConnection.err
notify.Name
TestConnectionHeader
notify.Name.test
handler.accumulator
sequence.name
cancel.Invoke
verifyResults.val
binder.runTest
cancel
call.Invoke.test
echo.Invoke.test
collect.Name
verifyResults
binder.Bind.b
handler.Preempt.BlockStmt.name
binder
testConnection.ctx
notify.Invoke.test
handler.Handle.ctx
collect
async.Invoke.h
sequence.Invoke
call.Invoke
collect.Invoke.h
handler.Preempt.ctx
stacktest
cancelParams.ID
async.Invoke
collect.Invoke
handler.calls
testConnection.server
call.Name
echo.Invoke.ctx
collect.Invoke.ctx
cancel.Invoke.test
handler.conn
call.Name.test
testConnection.RangeStmt_2992.BlockStmt.BlockStmt.Elts.ctx
echo.Invoke.results
testConnection
testConnection.framer
handler.Handle
handler
echo.Invoke.t
call.expect
TestConnectionHeader.t
testConnection.RangeStmt_2992.BlockStmt.BlockStmt.client
echo.Invoke
collect.Invoke.err
verifyResults.expect
binder.Bind
notify.method
sequence
cancel.Name.test
binder.Bind.h
handler.Preempt.req
invoker
testConnection.RangeStmt_2992.test
cancel.Invoke.err
newResults.BlockStmt.RangeStmt_5789.v
handler.Preempt.BlockStmt.params
handler.Handle.BlockStmt.waitFor
testConnection.listener
cancel.Name
call.Invoke.h
handler.Preempt.h
jsonrpc2
notify.params
verifyResults.results
async.Invoke.ctx
sequence.Invoke.test
cancel.Invoke.ctx
sequence.Name.test
newResults.expect
collect.fails
call.Invoke.results
notify.Invoke.h
echo.Invoke.h
sequence.Invoke.t
handler.Preempt.BlockStmt.err
collect.expect
echo
call.Invoke.err
collect.Invoke.test
sequence.Invoke.RangeStmt_5516.child
handler.waiters
async.method
sequence.tests
TestConnectionRaw.t
async.Invoke.test
collect.Invoke.t
collect.Invoke.results
binder.Bind.conn
binder.framer
async.name
handler.Handle.BlockStmt.err
async.Name.test
newResults
verifyResults.method
handler.Preempt
eventtest
call.Invoke.ctx
Members
X