.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools / gopls@v0.6.9 / internal / regtest / env.go
1 // Copyright 2020 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
5 package regtest
6
7 import (
8         "context"
9         "fmt"
10         "strings"
11         "sync"
12         "testing"
13
14         "golang.org/x/tools/internal/jsonrpc2/servertest"
15         "golang.org/x/tools/internal/lsp/fake"
16         "golang.org/x/tools/internal/lsp/protocol"
17 )
18
19 // Env holds an initialized fake Editor, Workspace, and Server, which may be
20 // used for writing tests. It also provides adapter methods that call t.Fatal
21 // on any error, so that tests for the happy path may be written without
22 // checking errors.
23 type Env struct {
24         T   *testing.T
25         Ctx context.Context
26
27         // Most tests should not need to access the scratch area, editor, server, or
28         // connection, but they are available if needed.
29         Sandbox *fake.Sandbox
30         Editor  *fake.Editor
31         Server  servertest.Connector
32
33         // mu guards the fields below, for the purpose of checking conditions on
34         // every change to diagnostics.
35         mu sync.Mutex
36         // For simplicity, each waiter gets a unique ID.
37         nextWaiterID int
38         state        State
39         waiters      map[int]*condition
40 }
41
42 // State encapsulates the server state TODO: explain more
43 type State struct {
44         // diagnostics are a map of relative path->diagnostics params
45         diagnostics        map[string]*protocol.PublishDiagnosticsParams
46         logs               []*protocol.LogMessageParams
47         showMessage        []*protocol.ShowMessageParams
48         showMessageRequest []*protocol.ShowMessageRequestParams
49
50         registrations   []*protocol.RegistrationParams
51         unregistrations []*protocol.UnregistrationParams
52
53         // outstandingWork is a map of token->work summary. All tokens are assumed to
54         // be string, though the spec allows for numeric tokens as well.  When work
55         // completes, it is deleted from this map.
56         outstandingWork map[protocol.ProgressToken]*workProgress
57         completedWork   map[string]uint64
58 }
59
60 type workProgress struct {
61         title, msg string
62         percent    float64
63 }
64
65 func (s State) String() string {
66         var b strings.Builder
67         b.WriteString("#### log messages (see RPC logs for full text):\n")
68         for _, msg := range s.logs {
69                 summary := fmt.Sprintf("%v: %q", msg.Type, msg.Message)
70                 if len(summary) > 60 {
71                         summary = summary[:57] + "..."
72                 }
73                 // Some logs are quite long, and since they should be reproduced in the RPC
74                 // logs on any failure we include here just a short summary.
75                 fmt.Fprint(&b, "\t"+summary+"\n")
76         }
77         b.WriteString("\n")
78         b.WriteString("#### diagnostics:\n")
79         for name, params := range s.diagnostics {
80                 fmt.Fprintf(&b, "\t%s (version %d):\n", name, int(params.Version))
81                 for _, d := range params.Diagnostics {
82                         fmt.Fprintf(&b, "\t\t(%d, %d): %s\n", int(d.Range.Start.Line), int(d.Range.Start.Character), d.Message)
83                 }
84         }
85         b.WriteString("\n")
86         b.WriteString("#### outstanding work:\n")
87         for token, state := range s.outstandingWork {
88                 name := state.title
89                 if name == "" {
90                         name = fmt.Sprintf("!NO NAME(token: %s)", token)
91                 }
92                 fmt.Fprintf(&b, "\t%s: %.2f\n", name, state.percent)
93         }
94         b.WriteString("#### completed work:\n")
95         for name, count := range s.completedWork {
96                 fmt.Fprintf(&b, "\t%s: %d\n", name, count)
97         }
98         return b.String()
99 }
100
101 // A condition is satisfied when all expectations are simultaneously
102 // met. At that point, the 'met' channel is closed. On any failure, err is set
103 // and the failed channel is closed.
104 type condition struct {
105         expectations []Expectation
106         verdict      chan Verdict
107 }
108
109 // NewEnv creates a new test environment using the given scratch environment
110 // and gopls server.
111 func NewEnv(ctx context.Context, t *testing.T, sandbox *fake.Sandbox, ts servertest.Connector, editorConfig fake.EditorConfig, withHooks bool) *Env {
112         t.Helper()
113         conn := ts.Connect(ctx)
114         env := &Env{
115                 T:       t,
116                 Ctx:     ctx,
117                 Sandbox: sandbox,
118                 Server:  ts,
119                 state: State{
120                         diagnostics:     make(map[string]*protocol.PublishDiagnosticsParams),
121                         outstandingWork: make(map[protocol.ProgressToken]*workProgress),
122                         completedWork:   make(map[string]uint64),
123                 },
124                 waiters: make(map[int]*condition),
125         }
126         var hooks fake.ClientHooks
127         if withHooks {
128                 hooks = fake.ClientHooks{
129                         OnDiagnostics:            env.onDiagnostics,
130                         OnLogMessage:             env.onLogMessage,
131                         OnWorkDoneProgressCreate: env.onWorkDoneProgressCreate,
132                         OnProgress:               env.onProgress,
133                         OnShowMessage:            env.onShowMessage,
134                         OnShowMessageRequest:     env.onShowMessageRequest,
135                         OnRegistration:           env.onRegistration,
136                         OnUnregistration:         env.onUnregistration,
137                 }
138         }
139         editor, err := fake.NewEditor(sandbox, editorConfig).Connect(ctx, conn, hooks)
140         if err != nil {
141                 t.Fatal(err)
142         }
143         env.Editor = editor
144         return env
145 }
146
147 func (e *Env) onDiagnostics(_ context.Context, d *protocol.PublishDiagnosticsParams) error {
148         e.mu.Lock()
149         defer e.mu.Unlock()
150
151         pth := e.Sandbox.Workdir.URIToPath(d.URI)
152         e.state.diagnostics[pth] = d
153         e.checkConditionsLocked()
154         return nil
155 }
156
157 func (e *Env) onShowMessage(_ context.Context, m *protocol.ShowMessageParams) error {
158         e.mu.Lock()
159         defer e.mu.Unlock()
160
161         e.state.showMessage = append(e.state.showMessage, m)
162         e.checkConditionsLocked()
163         return nil
164 }
165
166 func (e *Env) onShowMessageRequest(_ context.Context, m *protocol.ShowMessageRequestParams) error {
167         e.mu.Lock()
168         defer e.mu.Unlock()
169
170         e.state.showMessageRequest = append(e.state.showMessageRequest, m)
171         e.checkConditionsLocked()
172         return nil
173 }
174
175 func (e *Env) onLogMessage(_ context.Context, m *protocol.LogMessageParams) error {
176         e.mu.Lock()
177         defer e.mu.Unlock()
178
179         e.state.logs = append(e.state.logs, m)
180         e.checkConditionsLocked()
181         return nil
182 }
183
184 func (e *Env) onWorkDoneProgressCreate(_ context.Context, m *protocol.WorkDoneProgressCreateParams) error {
185         e.mu.Lock()
186         defer e.mu.Unlock()
187
188         e.state.outstandingWork[m.Token] = &workProgress{}
189         return nil
190 }
191
192 func (e *Env) onProgress(_ context.Context, m *protocol.ProgressParams) error {
193         e.mu.Lock()
194         defer e.mu.Unlock()
195         work, ok := e.state.outstandingWork[m.Token]
196         if !ok {
197                 panic(fmt.Sprintf("got progress report for unknown report %v: %v", m.Token, m))
198         }
199         v := m.Value.(map[string]interface{})
200         switch kind := v["kind"]; kind {
201         case "begin":
202                 work.title = v["title"].(string)
203                 if msg, ok := v["message"]; ok {
204                         work.msg = msg.(string)
205                 }
206         case "report":
207                 if pct, ok := v["percentage"]; ok {
208                         work.percent = pct.(float64)
209                 }
210                 if msg, ok := v["message"]; ok {
211                         work.msg = msg.(string)
212                 }
213         case "end":
214                 title := e.state.outstandingWork[m.Token].title
215                 e.state.completedWork[title] = e.state.completedWork[title] + 1
216                 delete(e.state.outstandingWork, m.Token)
217         }
218         e.checkConditionsLocked()
219         return nil
220 }
221
222 func (e *Env) onRegistration(_ context.Context, m *protocol.RegistrationParams) error {
223         e.mu.Lock()
224         defer e.mu.Unlock()
225
226         e.state.registrations = append(e.state.registrations, m)
227         e.checkConditionsLocked()
228         return nil
229 }
230
231 func (e *Env) onUnregistration(_ context.Context, m *protocol.UnregistrationParams) error {
232         e.mu.Lock()
233         defer e.mu.Unlock()
234
235         e.state.unregistrations = append(e.state.unregistrations, m)
236         e.checkConditionsLocked()
237         return nil
238 }
239
240 func (e *Env) checkConditionsLocked() {
241         for id, condition := range e.waiters {
242                 if v, _ := checkExpectations(e.state, condition.expectations); v != Unmet {
243                         delete(e.waiters, id)
244                         condition.verdict <- v
245                 }
246         }
247 }
248
249 // checkExpectations reports whether s meets all expectations.
250 func checkExpectations(s State, expectations []Expectation) (Verdict, string) {
251         finalVerdict := Met
252         var summary strings.Builder
253         for _, e := range expectations {
254                 v := e.Check(s)
255                 if v > finalVerdict {
256                         finalVerdict = v
257                 }
258                 summary.WriteString(fmt.Sprintf("\t%v: %s\n", v, e.Description()))
259         }
260         return finalVerdict, summary.String()
261 }
262
263 // DiagnosticsFor returns the current diagnostics for the file. It is useful
264 // after waiting on AnyDiagnosticAtCurrentVersion, when the desired diagnostic
265 // is not simply described by DiagnosticAt.
266 func (e *Env) DiagnosticsFor(name string) *protocol.PublishDiagnosticsParams {
267         e.mu.Lock()
268         defer e.mu.Unlock()
269         return e.state.diagnostics[name]
270 }
271
272 // Await waits for all expectations to simultaneously be met. It should only be
273 // called from the main test goroutine.
274 func (e *Env) Await(expectations ...Expectation) {
275         e.T.Helper()
276         e.mu.Lock()
277         // Before adding the waiter, we check if the condition is currently met or
278         // failed to avoid a race where the condition was realized before Await was
279         // called.
280         switch verdict, summary := checkExpectations(e.state, expectations); verdict {
281         case Met:
282                 e.mu.Unlock()
283                 return
284         case Unmeetable:
285                 e.mu.Unlock()
286                 e.T.Fatalf("unmeetable expectations:\n%s\nstate:\n%v", summary, e.state)
287         }
288         cond := &condition{
289                 expectations: expectations,
290                 verdict:      make(chan Verdict),
291         }
292         e.waiters[e.nextWaiterID] = cond
293         e.nextWaiterID++
294         e.mu.Unlock()
295
296         var err error
297         select {
298         case <-e.Ctx.Done():
299                 err = e.Ctx.Err()
300         case v := <-cond.verdict:
301                 if v != Met {
302                         err = fmt.Errorf("condition has final verdict %v", v)
303                 }
304         }
305         e.mu.Lock()
306         defer e.mu.Unlock()
307         _, summary := checkExpectations(e.state, expectations)
308
309         // Debugging an unmet expectation can be tricky, so we put some effort into
310         // nicely formatting the failure.
311         if err != nil {
312                 e.T.Fatalf("waiting on:\n%s\nerr:%v\n\nstate:\n%v", summary, err, e.state)
313         }
314 }