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