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 / expectation.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         "fmt"
9         "regexp"
10         "strings"
11
12         "golang.org/x/tools/internal/lsp"
13         "golang.org/x/tools/internal/lsp/protocol"
14         "golang.org/x/tools/internal/span"
15 )
16
17 // An Expectation asserts that the state of the editor at a point in time
18 // matches an expected condition. This is used for signaling in tests when
19 // certain conditions in the editor are met.
20 type Expectation interface {
21         // Check determines whether the state of the editor satisfies the
22         // expectation, returning the results that met the condition.
23         Check(State) Verdict
24         // Description is a human-readable description of the expectation.
25         Description() string
26 }
27
28 var (
29         // InitialWorkspaceLoad is an expectation that the workspace initial load has
30         // completed. It is verified via workdone reporting.
31         InitialWorkspaceLoad = CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1)
32 )
33
34 // A Verdict is the result of checking an expectation against the current
35 // editor state.
36 type Verdict int
37
38 // Order matters for the following constants: verdicts are sorted in order of
39 // decisiveness.
40 const (
41         // Met indicates that an expectation is satisfied by the current state.
42         Met Verdict = iota
43         // Unmet indicates that an expectation is not currently met, but could be met
44         // in the future.
45         Unmet
46         // Unmeetable indicates that an expectation cannot be satisfied in the
47         // future.
48         Unmeetable
49 )
50
51 func (v Verdict) String() string {
52         switch v {
53         case Met:
54                 return "Met"
55         case Unmet:
56                 return "Unmet"
57         case Unmeetable:
58                 return "Unmeetable"
59         }
60         return fmt.Sprintf("unrecognized verdict %d", v)
61 }
62
63 // SimpleExpectation holds an arbitrary check func, and implements the Expectation interface.
64 type SimpleExpectation struct {
65         check       func(State) Verdict
66         description string
67 }
68
69 // Check invokes e.check.
70 func (e SimpleExpectation) Check(s State) Verdict {
71         return e.check(s)
72 }
73
74 // Description returns e.descriptin.
75 func (e SimpleExpectation) Description() string {
76         return e.description
77 }
78
79 // OnceMet returns an Expectation that, once the precondition is met, asserts
80 // that mustMeet is met.
81 func OnceMet(precondition Expectation, mustMeet Expectation) *SimpleExpectation {
82         check := func(s State) Verdict {
83                 switch pre := precondition.Check(s); pre {
84                 case Unmeetable:
85                         return Unmeetable
86                 case Met:
87                         verdict := mustMeet.Check(s)
88                         if verdict != Met {
89                                 return Unmeetable
90                         }
91                         return Met
92                 default:
93                         return Unmet
94                 }
95         }
96         return &SimpleExpectation{
97                 check:       check,
98                 description: fmt.Sprintf("once %q is met, must have %q", precondition.Description(), mustMeet.Description()),
99         }
100 }
101
102 // ReadDiagnostics is an 'expectation' that is used to read diagnostics
103 // atomically. It is intended to be used with 'OnceMet'.
104 func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) *SimpleExpectation {
105         check := func(s State) Verdict {
106                 diags, ok := s.diagnostics[fileName]
107                 if !ok {
108                         return Unmeetable
109                 }
110                 *into = *diags
111                 return Met
112         }
113         return &SimpleExpectation{
114                 check:       check,
115                 description: fmt.Sprintf("read diagnostics for %q", fileName),
116         }
117 }
118
119 // NoOutstandingWork asserts that there is no work initiated using the LSP
120 // $/progress API that has not completed.
121 func NoOutstandingWork() SimpleExpectation {
122         check := func(s State) Verdict {
123                 if len(s.outstandingWork) == 0 {
124                         return Met
125                 }
126                 return Unmet
127         }
128         return SimpleExpectation{
129                 check:       check,
130                 description: "no outstanding work",
131         }
132 }
133
134 // NoShowMessage asserts that the editor has not received a ShowMessage.
135 func NoShowMessage() SimpleExpectation {
136         check := func(s State) Verdict {
137                 if len(s.showMessage) == 0 {
138                         return Met
139                 }
140                 return Unmeetable
141         }
142         return SimpleExpectation{
143                 check:       check,
144                 description: "no ShowMessage received",
145         }
146 }
147
148 // ShownMessage asserts that the editor has received a ShownMessage with the
149 // given title.
150 func ShownMessage(title string) SimpleExpectation {
151         check := func(s State) Verdict {
152                 for _, m := range s.showMessage {
153                         if strings.Contains(m.Message, title) {
154                                 return Met
155                         }
156                 }
157                 return Unmet
158         }
159         return SimpleExpectation{
160                 check:       check,
161                 description: "received ShowMessage",
162         }
163 }
164
165 // ShowMessageRequest asserts that the editor has received a ShowMessageRequest
166 // with an action item that has the given title.
167 func ShowMessageRequest(title string) SimpleExpectation {
168         check := func(s State) Verdict {
169                 if len(s.showMessageRequest) == 0 {
170                         return Unmet
171                 }
172                 // Only check the most recent one.
173                 m := s.showMessageRequest[len(s.showMessageRequest)-1]
174                 if len(m.Actions) == 0 || len(m.Actions) > 1 {
175                         return Unmet
176                 }
177                 if m.Actions[0].Title == title {
178                         return Met
179                 }
180                 return Unmet
181         }
182         return SimpleExpectation{
183                 check:       check,
184                 description: "received ShowMessageRequest",
185         }
186 }
187
188 // CompletedWork expects a work item to have been completed >= atLeast times.
189 //
190 // Since the Progress API doesn't include any hidden metadata, we must use the
191 // progress notification title to identify the work we expect to be completed.
192 func CompletedWork(title string, atLeast int) SimpleExpectation {
193         check := func(s State) Verdict {
194                 if s.completedWork[title] >= atLeast {
195                         return Met
196                 }
197                 return Unmet
198         }
199         return SimpleExpectation{
200                 check:       check,
201                 description: fmt.Sprintf("completed work %q at least %d time(s)", title, atLeast),
202         }
203 }
204
205 // LogExpectation is an expectation on the log messages received by the editor
206 // from gopls.
207 type LogExpectation struct {
208         check       func([]*protocol.LogMessageParams) Verdict
209         description string
210 }
211
212 // Check implements the Expectation interface.
213 func (e LogExpectation) Check(s State) Verdict {
214         return e.check(s.logs)
215 }
216
217 // Description implements the Expectation interface.
218 func (e LogExpectation) Description() string {
219         return e.description
220 }
221
222 // NoErrorLogs asserts that the client has not received any log messages of
223 // error severity.
224 func NoErrorLogs() LogExpectation {
225         return NoLogMatching(protocol.Error, "")
226 }
227
228 // LogMatching asserts that the client has received a log message
229 // of type typ matching the regexp re.
230 func LogMatching(typ protocol.MessageType, re string, count int) LogExpectation {
231         rec, err := regexp.Compile(re)
232         if err != nil {
233                 panic(err)
234         }
235         check := func(msgs []*protocol.LogMessageParams) Verdict {
236                 var found int
237                 for _, msg := range msgs {
238                         if msg.Type == typ && rec.Match([]byte(msg.Message)) {
239                                 found++
240                         }
241                 }
242                 if found == count {
243                         return Met
244                 }
245                 return Unmet
246         }
247         return LogExpectation{
248                 check:       check,
249                 description: fmt.Sprintf("log message matching %q", re),
250         }
251 }
252
253 // NoLogMatching asserts that the client has not received a log message
254 // of type typ matching the regexp re. If re is an empty string, any log
255 // message is considered a match.
256 func NoLogMatching(typ protocol.MessageType, re string) LogExpectation {
257         var r *regexp.Regexp
258         if re != "" {
259                 var err error
260                 r, err = regexp.Compile(re)
261                 if err != nil {
262                         panic(err)
263                 }
264         }
265         check := func(msgs []*protocol.LogMessageParams) Verdict {
266                 for _, msg := range msgs {
267                         if msg.Type != typ {
268                                 continue
269                         }
270                         if r == nil || r.Match([]byte(msg.Message)) {
271                                 return Unmeetable
272                         }
273                 }
274                 return Met
275         }
276         return LogExpectation{
277                 check:       check,
278                 description: fmt.Sprintf("no log message matching %q", re),
279         }
280 }
281
282 // RegistrationExpectation is an expectation on the capability registrations
283 // received by the editor from gopls.
284 type RegistrationExpectation struct {
285         check       func([]*protocol.RegistrationParams) Verdict
286         description string
287 }
288
289 // Check implements the Expectation interface.
290 func (e RegistrationExpectation) Check(s State) Verdict {
291         return e.check(s.registrations)
292 }
293
294 // Description implements the Expectation interface.
295 func (e RegistrationExpectation) Description() string {
296         return e.description
297 }
298
299 // RegistrationMatching asserts that the client has received a capability
300 // registration matching the given regexp.
301 func RegistrationMatching(re string) RegistrationExpectation {
302         rec, err := regexp.Compile(re)
303         if err != nil {
304                 panic(err)
305         }
306         check := func(params []*protocol.RegistrationParams) Verdict {
307                 for _, p := range params {
308                         for _, r := range p.Registrations {
309                                 if rec.Match([]byte(r.Method)) {
310                                         return Met
311                                 }
312                         }
313                 }
314                 return Unmet
315         }
316         return RegistrationExpectation{
317                 check:       check,
318                 description: fmt.Sprintf("registration matching %q", re),
319         }
320 }
321
322 // UnregistrationExpectation is an expectation on the capability
323 // unregistrations received by the editor from gopls.
324 type UnregistrationExpectation struct {
325         check       func([]*protocol.UnregistrationParams) Verdict
326         description string
327 }
328
329 // Check implements the Expectation interface.
330 func (e UnregistrationExpectation) Check(s State) Verdict {
331         return e.check(s.unregistrations)
332 }
333
334 // Description implements the Expectation interface.
335 func (e UnregistrationExpectation) Description() string {
336         return e.description
337 }
338
339 // UnregistrationMatching asserts that the client has received an
340 // unregistration whose ID matches the given regexp.
341 func UnregistrationMatching(re string) UnregistrationExpectation {
342         rec, err := regexp.Compile(re)
343         if err != nil {
344                 panic(err)
345         }
346         check := func(params []*protocol.UnregistrationParams) Verdict {
347                 for _, p := range params {
348                         for _, r := range p.Unregisterations {
349                                 if rec.Match([]byte(r.Method)) {
350                                         return Met
351                                 }
352                         }
353                 }
354                 return Unmet
355         }
356         return UnregistrationExpectation{
357                 check:       check,
358                 description: fmt.Sprintf("unregistration matching %q", re),
359         }
360 }
361
362 // A DiagnosticExpectation is a condition that must be met by the current set
363 // of diagnostics for a file.
364 type DiagnosticExpectation struct {
365         // IsMet determines whether the diagnostics for this file version satisfy our
366         // expectation.
367         isMet func(*protocol.PublishDiagnosticsParams) bool
368         // Description is a human-readable description of the diagnostic expectation.
369         description string
370         // Path is the scratch workdir-relative path to the file being asserted on.
371         path string
372 }
373
374 // Check implements the Expectation interface.
375 func (e DiagnosticExpectation) Check(s State) Verdict {
376         if diags, ok := s.diagnostics[e.path]; ok && e.isMet(diags) {
377                 return Met
378         }
379         return Unmet
380 }
381
382 // Description implements the Expectation interface.
383 func (e DiagnosticExpectation) Description() string {
384         return fmt.Sprintf("%s: %s", e.path, e.description)
385 }
386
387 // EmptyDiagnostics asserts that empty diagnostics are sent for the
388 // workspace-relative path name.
389 func EmptyDiagnostics(name string) Expectation {
390         check := func(s State) Verdict {
391                 if diags := s.diagnostics[name]; diags != nil && len(diags.Diagnostics) == 0 {
392                         return Met
393                 }
394                 return Unmet
395         }
396         return SimpleExpectation{
397                 check:       check,
398                 description: "empty diagnostics",
399         }
400 }
401
402 // NoDiagnostics asserts that no diagnostics are sent for the
403 // workspace-relative path name. It should be used primarily in conjunction
404 // with a OnceMet, as it has to check that all outstanding diagnostics have
405 // already been delivered.
406 func NoDiagnostics(name string) Expectation {
407         check := func(s State) Verdict {
408                 if _, ok := s.diagnostics[name]; !ok {
409                         return Met
410                 }
411                 return Unmet
412         }
413         return SimpleExpectation{
414                 check:       check,
415                 description: "no diagnostics",
416         }
417 }
418
419 // AnyDiagnosticAtCurrentVersion asserts that there is a diagnostic report for
420 // the current edited version of the buffer corresponding to the given
421 // workdir-relative pathname.
422 func (e *Env) AnyDiagnosticAtCurrentVersion(name string) DiagnosticExpectation {
423         version := e.Editor.BufferVersion(name)
424         isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
425                 return int(diags.Version) == version
426         }
427         return DiagnosticExpectation{
428                 isMet:       isMet,
429                 description: fmt.Sprintf("any diagnostics at version %d", version),
430                 path:        name,
431         }
432 }
433
434 // DiagnosticAtRegexp expects that there is a diagnostic entry at the start
435 // position matching the regexp search string re in the buffer specified by
436 // name. Note that this currently ignores the end position.
437 func (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation {
438         e.T.Helper()
439         pos := e.RegexpSearch(name, re)
440         expectation := DiagnosticAt(name, pos.Line, pos.Column)
441         expectation.description += fmt.Sprintf(" (location of %q)", re)
442         return expectation
443 }
444
445 // DiagnosticAt asserts that there is a diagnostic entry at the position
446 // specified by line and col, for the workdir-relative path name.
447 func DiagnosticAt(name string, line, col int) DiagnosticExpectation {
448         isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
449                 for _, d := range diags.Diagnostics {
450                         if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
451                                 return true
452                         }
453                 }
454                 return false
455         }
456         return DiagnosticExpectation{
457                 isMet:       isMet,
458                 description: fmt.Sprintf("diagnostic at {line:%d, column:%d}", line, col),
459                 path:        name,
460         }
461 }
462
463 // NoDiagnosticAtRegexp expects that there is no diagnostic entry at the start
464 // position matching the regexp search string re in the buffer specified by
465 // name. Note that this currently ignores the end position.
466 // This should only be used in combination with OnceMet for a given condition,
467 // otherwise it may always succeed.
468 func (e *Env) NoDiagnosticAtRegexp(name, re string) DiagnosticExpectation {
469         e.T.Helper()
470         pos := e.RegexpSearch(name, re)
471         expectation := NoDiagnosticAt(name, pos.Line, pos.Column)
472         expectation.description += fmt.Sprintf(" (location of %q)", re)
473         return expectation
474 }
475
476 // NoDiagnosticAt asserts that there is no diagnostic entry at the position
477 // specified by line and col, for the workdir-relative path name.
478 // This should only be used in combination with OnceMet for a given condition,
479 // otherwise it may always succeed.
480 func NoDiagnosticAt(name string, line, col int) DiagnosticExpectation {
481         isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
482                 for _, d := range diags.Diagnostics {
483                         if d.Range.Start.Line == float64(line) && d.Range.Start.Character == float64(col) {
484                                 return false
485                         }
486                 }
487                 return true
488         }
489         return DiagnosticExpectation{
490                 isMet:       isMet,
491                 description: fmt.Sprintf("no diagnostic at {line:%d, column:%d}", line, col),
492                 path:        name,
493         }
494 }
495
496 // NoDiagnosticWithMessage asserts that there is no diagnostic entry with the
497 // given message.
498 //
499 // This should only be used in combination with OnceMet for a given condition,
500 // otherwise it may always succeed.
501 func NoDiagnosticWithMessage(msg string) DiagnosticExpectation {
502         var uri span.URI
503         isMet := func(diags *protocol.PublishDiagnosticsParams) bool {
504                 for _, d := range diags.Diagnostics {
505                         if d.Message == msg {
506                                 return true
507                         }
508                 }
509                 return false
510         }
511         var path string
512         if uri != "" {
513                 path = uri.Filename()
514         }
515         return DiagnosticExpectation{
516                 isMet:       isMet,
517                 description: fmt.Sprintf("no diagnostic with message %s", msg),
518                 path:        path,
519         }
520 }