.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools / gopls@v0.6.9 / internal / regtest / completion / completion_test.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 completion
6
7 import (
8         "fmt"
9         "strings"
10         "testing"
11
12         . "golang.org/x/tools/gopls/internal/regtest"
13
14         "golang.org/x/tools/internal/lsp/fake"
15         "golang.org/x/tools/internal/lsp/protocol"
16         "golang.org/x/tools/internal/testenv"
17 )
18
19 func TestMain(m *testing.M) {
20         Main(m)
21 }
22
23 const proxy = `
24 -- example.com@v1.2.3/go.mod --
25 module example.com
26
27 go 1.12
28 -- example.com@v1.2.3/blah/blah.go --
29 package blah
30
31 const Name = "Blah"
32 -- random.org@v1.2.3/go.mod --
33 module random.org
34
35 go 1.12
36 -- random.org@v1.2.3/blah/blah.go --
37 package hello
38
39 const Name = "Hello"
40 `
41
42 func TestPackageCompletion(t *testing.T) {
43         testenv.NeedsGo1Point(t, 14)
44         const files = `
45 -- go.mod --
46 module mod.com
47
48 go 1.12
49 -- fruits/apple.go --
50 package apple
51
52 fun apple() int {
53         return 0
54 }
55
56 -- fruits/testfile.go --
57 // this is a comment
58
59 /*
60  this is a multiline comment
61 */
62
63 import "fmt"
64
65 func test() {}
66
67 -- fruits/testfile2.go --
68 package
69
70 -- fruits/testfile3.go --
71 pac
72 `
73         var (
74                 testfile4 = ""
75                 testfile5 = "/*a comment*/ "
76                 testfile6 = "/*a comment*/\n"
77         )
78         for _, tc := range []struct {
79                 name          string
80                 filename      string
81                 content       *string
82                 triggerRegexp string
83                 want          []string
84                 editRegexp    string
85         }{
86                 {
87                         name:          "package completion at valid position",
88                         filename:      "fruits/testfile.go",
89                         triggerRegexp: "\n()",
90                         want:          []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
91                         editRegexp:    "\n()",
92                 },
93                 {
94                         name:          "package completion in a comment",
95                         filename:      "fruits/testfile.go",
96                         triggerRegexp: "th(i)s",
97                         want:          nil,
98                 },
99                 {
100                         name:          "package completion in a multiline comment",
101                         filename:      "fruits/testfile.go",
102                         triggerRegexp: `\/\*\n()`,
103                         want:          nil,
104                 },
105                 {
106                         name:          "package completion at invalid position",
107                         filename:      "fruits/testfile.go",
108                         triggerRegexp: "import \"fmt\"\n()",
109                         want:          nil,
110                 },
111                 {
112                         name:          "package completion after keyword 'package'",
113                         filename:      "fruits/testfile2.go",
114                         triggerRegexp: "package()",
115                         want:          []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
116                         editRegexp:    "package\n",
117                 },
118                 {
119                         name:          "package completion with 'pac' prefix",
120                         filename:      "fruits/testfile3.go",
121                         triggerRegexp: "pac()",
122                         want:          []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
123                         editRegexp:    "pac",
124                 },
125                 {
126                         name:          "package completion for empty file",
127                         filename:      "fruits/testfile4.go",
128                         triggerRegexp: "^$",
129                         content:       &testfile4,
130                         want:          []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
131                         editRegexp:    "^$",
132                 },
133                 {
134                         name:          "package completion without terminal newline",
135                         filename:      "fruits/testfile5.go",
136                         triggerRegexp: `\*\/ ()`,
137                         content:       &testfile5,
138                         want:          []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
139                         editRegexp:    `\*\/ ()`,
140                 },
141                 {
142                         name:          "package completion on terminal newline",
143                         filename:      "fruits/testfile6.go",
144                         triggerRegexp: `\*\/\n()`,
145                         content:       &testfile6,
146                         want:          []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
147                         editRegexp:    `\*\/\n()`,
148                 },
149         } {
150                 t.Run(tc.name, func(t *testing.T) {
151                         Run(t, files, func(t *testing.T, env *Env) {
152                                 if tc.content != nil {
153                                         env.WriteWorkspaceFile(tc.filename, *tc.content)
154                                         env.Await(
155                                                 env.DoneWithChangeWatchedFiles(),
156                                         )
157                                 }
158                                 env.OpenFile(tc.filename)
159                                 completions := env.Completion(tc.filename, env.RegexpSearch(tc.filename, tc.triggerRegexp))
160
161                                 // Check that the completion item suggestions are in the range
162                                 // of the file.
163                                 lineCount := len(strings.Split(env.Editor.BufferText(tc.filename), "\n"))
164                                 for _, item := range completions.Items {
165                                         if start := int(item.TextEdit.Range.Start.Line); start >= lineCount {
166                                                 t.Fatalf("unexpected text edit range start line number: got %d, want less than %d", start, lineCount)
167                                         }
168                                         if end := int(item.TextEdit.Range.End.Line); end >= lineCount {
169                                                 t.Fatalf("unexpected text edit range end line number: got %d, want less than %d", end, lineCount)
170                                         }
171                                 }
172
173                                 if tc.want != nil {
174                                         start, end := env.RegexpRange(tc.filename, tc.editRegexp)
175                                         expectedRng := protocol.Range{
176                                                 Start: fake.Pos.ToProtocolPosition(start),
177                                                 End:   fake.Pos.ToProtocolPosition(end),
178                                         }
179                                         for _, item := range completions.Items {
180                                                 gotRng := item.TextEdit.Range
181                                                 if expectedRng != gotRng {
182                                                         t.Errorf("unexpected completion range for completion item %s: got %v, want %v",
183                                                                 item.Label, gotRng, expectedRng)
184                                                 }
185                                         }
186                                 }
187
188                                 diff := compareCompletionResults(tc.want, completions.Items)
189                                 if diff != "" {
190                                         t.Error(diff)
191                                 }
192                         })
193                 })
194         }
195 }
196
197 func TestPackageNameCompletion(t *testing.T) {
198         const files = `
199 -- go.mod --
200 module mod.com
201
202 go 1.12
203 -- math/add.go --
204 package ma
205 `
206
207         want := []string{"ma", "ma_test", "main", "math", "math_test"}
208         Run(t, files, func(t *testing.T, env *Env) {
209                 env.OpenFile("math/add.go")
210                 completions := env.Completion("math/add.go", fake.Pos{
211                         Line:   0,
212                         Column: 10,
213                 })
214
215                 diff := compareCompletionResults(want, completions.Items)
216                 if diff != "" {
217                         t.Fatal(diff)
218                 }
219         })
220 }
221
222 func compareCompletionResults(want []string, gotItems []protocol.CompletionItem) string {
223         if len(gotItems) != len(want) {
224                 return fmt.Sprintf("got %v completion(s), want %v", len(gotItems), len(want))
225         }
226
227         var got []string
228         for _, item := range gotItems {
229                 got = append(got, item.Label)
230         }
231
232         for i, v := range got {
233                 if v != want[i] {
234                         return fmt.Sprintf("completion results are not the same: got %v, want %v", got, want)
235                 }
236         }
237
238         return ""
239 }
240
241 func TestUnimportedCompletion(t *testing.T) {
242         testenv.NeedsGo1Point(t, 14)
243
244         const mod = `
245 -- go.mod --
246 module mod.com
247
248 go 1.14
249
250 require example.com v1.2.3
251 -- go.sum --
252 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
253 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
254 -- main.go --
255 package main
256
257 func main() {
258         _ = blah
259 }
260 -- main2.go --
261 package main
262
263 import "example.com/blah"
264
265 func _() {
266         _ = blah.Hello
267 }
268 `
269         WithOptions(
270                 ProxyFiles(proxy),
271         ).Run(t, mod, func(t *testing.T, env *Env) {
272                 // Make sure the dependency is in the module cache and accessible for
273                 // unimported completions, and then remove it before proceeding.
274                 env.RemoveWorkspaceFile("main2.go")
275                 env.RunGoCommand("mod", "tidy")
276                 env.Await(env.DoneWithChangeWatchedFiles())
277
278                 // Trigger unimported completions for the example.com/blah package.
279                 env.OpenFile("main.go")
280                 env.Await(env.DoneWithOpen())
281                 pos := env.RegexpSearch("main.go", "ah")
282                 completions := env.Completion("main.go", pos)
283                 if len(completions.Items) == 0 {
284                         t.Fatalf("no completion items")
285                 }
286                 env.AcceptCompletion("main.go", pos, completions.Items[0])
287                 env.Await(env.DoneWithChange())
288
289                 // Trigger completions once again for the blah.<> selector.
290                 env.RegexpReplace("main.go", "_ = blah", "_ = blah.")
291                 env.Await(env.DoneWithChange())
292                 pos = env.RegexpSearch("main.go", "\n}")
293                 completions = env.Completion("main.go", pos)
294                 if len(completions.Items) != 1 {
295                         t.Fatalf("expected 1 completion item, got %v", len(completions.Items))
296                 }
297                 item := completions.Items[0]
298                 if item.Label != "Name" {
299                         t.Fatalf("expected completion item blah.Name, got %v", item.Label)
300                 }
301                 env.AcceptCompletion("main.go", pos, item)
302
303                 // Await the diagnostics to add example.com/blah to the go.mod file.
304                 env.SaveBufferWithoutActions("main.go")
305                 env.Await(
306                         env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
307                 )
308         })
309 }
310
311 // Test that completions still work with an undownloaded module, golang/go#43333.
312 func TestUndownloadedModule(t *testing.T) {
313         // mod.com depends on example.com, but only in a file that's hidden by a
314         // build tag, so the IWL won't download example.com. That will cause errors
315         // in the go list -m call performed by the imports package.
316         const files = `
317 -- go.mod --
318 module mod.com
319
320 go 1.14
321
322 require example.com v1.2.3
323 -- go.sum --
324 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
325 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
326 -- useblah.go --
327 // +build hidden
328
329 package pkg
330 import "example.com/blah"
331 var _ = blah.Name
332 -- mainmod/mainmod.go --
333 package mainmod
334
335 const Name = "mainmod"
336 `
337         WithOptions(ProxyFiles(proxy)).Run(t, files, func(t *testing.T, env *Env) {
338                 env.CreateBuffer("import.go", "package pkg\nvar _ = mainmod.Name\n")
339                 env.SaveBuffer("import.go")
340                 content := env.ReadWorkspaceFile("import.go")
341                 if !strings.Contains(content, `import "mod.com/mainmod`) {
342                         t.Errorf("expected import of mod.com/mainmod in %q", content)
343                 }
344         })
345 }
346
347 // Test that we can doctor the source code enough so the file is
348 // parseable and completion works as expected.
349 func TestSourceFixup(t *testing.T) {
350         const files = `
351 -- go.mod --
352 module mod.com
353
354 go 1.12
355 -- foo.go --
356 package foo
357
358 func _() {
359         var s S
360         if s.
361 }
362
363 type S struct {
364         i int
365 }
366 `
367
368         Run(t, files, func(t *testing.T, env *Env) {
369                 env.OpenFile("foo.go")
370                 completions := env.Completion("foo.go", env.RegexpSearch("foo.go", `if s\.()`))
371                 diff := compareCompletionResults([]string{"i"}, completions.Items)
372                 if diff != "" {
373                         t.Fatal(diff)
374                 }
375         })
376 }