Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools / gopls@v0.5.2 / 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]int
58 }
59
60 type workProgress struct {
61         title   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]int),
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         case "report":
204                 if pct, ok := v["percentage"]; ok {
205                         work.percent = pct.(float64)
206                 }
207         case "end":
208                 title := e.state.outstandingWork[m.Token].title
209                 e.state.completedWork[title] = e.state.completedWork[title] + 1
210                 delete(e.state.outstandingWork, m.Token)
211         }
212         e.checkConditionsLocked()
213         return nil
214 }
215
216 func (e *Env) onRegistration(_ context.Context, m *protocol.RegistrationParams) error {
217         e.mu.Lock()
218         defer e.mu.Unlock()
219
220         e.state.registrations = append(e.state.registrations, m)
221         e.checkConditionsLocked()
222         return nil
223 }
224
225 func (e *Env) onUnregistration(_ context.Context, m *protocol.UnregistrationParams) error {
226         e.mu.Lock()
227         defer e.mu.Unlock()
228
229         e.state.unregistrations = append(e.state.unregistrations, m)
230         e.checkConditionsLocked()
231         return nil
232 }
233
234 func (e *Env) checkConditionsLocked() {
235         for id, condition := range e.waiters {
236                 if v, _ := checkExpectations(e.state, condition.expectations); v != Unmet {
237                         delete(e.waiters, id)
238                         condition.verdict <- v
239                 }
240         }
241 }
242
243 // checkExpectations reports whether s meets all expectations.
244 func checkExpectations(s State, expectations []Expectation) (Verdict, string) {
245         finalVerdict := Met
246         var summary strings.Builder
247         for _, e := range expectations {
248                 v := e.Check(s)
249                 if v > finalVerdict {
250                         finalVerdict = v
251                 }
252                 summary.WriteString(fmt.Sprintf("\t%v: %s\n", v, e.Description()))
253         }
254         return finalVerdict, summary.String()
255 }
256
257 // DiagnosticsFor returns the current diagnostics for the file. It is useful
258 // after waiting on AnyDiagnosticAtCurrentVersion, when the desired diagnostic
259 // is not simply described by DiagnosticAt.
260 func (e *Env) DiagnosticsFor(name string) *protocol.PublishDiagnosticsParams {
261         e.mu.Lock()
262         defer e.mu.Unlock()
263         return e.state.diagnostics[name]
264 }
265
266 // Await waits for all expectations to simultaneously be met. It should only be
267 // called from the main test goroutine.
268 func (e *Env) Await(expectations ...Expectation) {
269         e.T.Helper()
270         e.mu.Lock()
271         // Before adding the waiter, we check if the condition is currently met or
272         // failed to avoid a race where the condition was realized before Await was
273         // called.
274         switch verdict, summary := checkExpectations(e.state, expectations); verdict {
275         case Met:
276                 e.mu.Unlock()
277                 return
278         case Unmeetable:
279                 e.mu.Unlock()
280                 e.T.Fatalf("unmeetable expectations:\n%s\nstate:\n%v", summary, e.state)
281         }
282         cond := &condition{
283                 expectations: expectations,
284                 verdict:      make(chan Verdict),
285         }
286         e.waiters[e.nextWaiterID] = cond
287         e.nextWaiterID++
288         e.mu.Unlock()
289
290         var err error
291         select {
292         case <-e.Ctx.Done():
293                 err = e.Ctx.Err()
294         case v := <-cond.verdict:
295                 if v != Met {
296                         err = fmt.Errorf("condition has final verdict %v", v)
297                 }
298         }
299         e.mu.Lock()
300         defer e.mu.Unlock()
301         _, summary := checkExpectations(e.state, expectations)
302
303         // Debugging an unmet expectation can be tricky, so we put some effort into
304         // nicely formatting the failure.
305         if err != nil {
306                 e.T.Fatalf("waiting on:\n%s\nerr:%v\n\nstate:\n%v", summary, err, e.state)
307         }
308 }