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
5package regtest
6
7import (
8	"context"
9	"fmt"
10	"log"
11	"os"
12	"testing"
13
14	"golang.org/x/tools/internal/lsp"
15	"golang.org/x/tools/internal/lsp/fake"
16	"golang.org/x/tools/internal/lsp/protocol"
17	"golang.org/x/tools/internal/lsp/tests"
18	"golang.org/x/tools/internal/testenv"
19)
20
21// Use mod.com for all go.mod files due to golang/go#35230.
22const exampleProgram = `
23-- go.mod --
24module mod.com
25
26go 1.12
27-- main.go --
28package main
29
30import "fmt"
31
32func main() {
33	fmt.Println("Hello World.")
34}`
35
36func TestDiagnosticErrorInEditedFile(t *testing.T) {
37	// This test is very basic: start with a clean Go program, make an error, and
38	// get a diagnostic for that error. However, it also demonstrates how to
39	// combine Expectations to await more complex state in the editor.
40	runner.Run(t, exampleProgram, func(t *testing.T, env *Env) {
41		// Deleting the 'n' at the end of Println should generate a single error
42		// diagnostic.
43		env.OpenFile("main.go")
44		env.RegexpReplace("main.go", "Printl(n)", "")
45		env.Await(
46			// Once we have gotten diagnostics for the change above, we should
47			// satisfy the DiagnosticAtRegexp assertion.
48			OnceMet(
49				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
50				env.DiagnosticAtRegexp("main.go", "Printl"),
51			),
52			// Assert that this test has sent no error logs to the client. This is not
53			// strictly necessary for testing this regression, but is included here
54			// as an example of using the NoErrorLogs() expectation. Feel free to
55			// delete.
56			NoErrorLogs(),
57		)
58	})
59}
60
61func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
62	const onlyMod = `
63-- go.mod --
64module mod.com
65
66go 1.12
67`
68	runner.Run(t, onlyMod, func(t *testing.T, env *Env) {
69		env.CreateBuffer("main.go", `package main
70
71func m() {
72	log.Println()
73}
74`)
75		env.Await(
76			env.DiagnosticAtRegexp("main.go", "log"),
77		)
78		env.SaveBuffer("main.go")
79		env.Await(
80			EmptyDiagnostics("main.go"),
81		)
82	})
83}
84
85func TestDiagnosticErrorInNewFile(t *testing.T) {
86	const brokenFile = `package main
87
88const Foo = "abc
89`
90	runner.Run(t, brokenFile, func(t *testing.T, env *Env) {
91		env.CreateBuffer("broken.go", brokenFile)
92		env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc"))
93	})
94}
95
96// badPackage contains a duplicate definition of the 'a' const.
97const badPackage = `
98-- go.mod --
99module mod.com
100
101go 1.12
102-- a.go --
103package consts
104
105const a = 1
106-- b.go --
107package consts
108
109const a = 2
110`
111
112func TestDiagnosticClearingOnEdit(t *testing.T) {
113	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
114		env.OpenFile("b.go")
115		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
116
117		// Fix the error by editing the const name in b.go to `b`.
118		env.RegexpReplace("b.go", "(a) = 2", "b")
119		env.Await(
120			EmptyDiagnostics("a.go"),
121			EmptyDiagnostics("b.go"),
122		)
123	})
124}
125
126func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) {
127	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
128		env.OpenFile("a.go")
129		env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
130		env.RemoveWorkspaceFile("b.go")
131
132		env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
133	})
134}
135
136func TestDiagnosticClearingOnClose(t *testing.T) {
137	runner.Run(t, badPackage, func(t *testing.T, env *Env) {
138		env.CreateBuffer("c.go", `package consts
139
140const a = 3`)
141		env.Await(
142			env.DiagnosticAtRegexp("a.go", "a = 1"),
143			env.DiagnosticAtRegexp("b.go", "a = 2"),
144			env.DiagnosticAtRegexp("c.go", "a = 3"))
145		env.CloseBuffer("c.go")
146		env.Await(
147			env.DiagnosticAtRegexp("a.go", "a = 1"),
148			env.DiagnosticAtRegexp("b.go", "a = 2"),
149			EmptyDiagnostics("c.go"))
150	})
151}
152
153// Tests golang/go#37978.
154func TestIssue37978(t *testing.T) {
155	runner.Run(t, exampleProgram, func(t *testing.T, env *Env) {
156		// Create a new workspace-level directory and empty file.
157		env.CreateBuffer("c/c.go", "")
158
159		// Write the file contents with a missing import.
160		env.EditBuffer("c/c.go", fake.Edit{
161			Text: `package c
162
163const a = http.MethodGet
164`,
165		})
166		env.Await(
167			env.DiagnosticAtRegexp("c/c.go", "http.MethodGet"),
168		)
169		// Save file, which will organize imports, adding the expected import.
170		// Expect the diagnostics to clear.
171		env.SaveBuffer("c/c.go")
172		env.Await(
173			EmptyDiagnostics("c/c.go"),
174		)
175	})
176}
177
178// Tests golang/go#38878: good a.go, bad a_test.go, remove a_test.go but its errors remain
179// If the file is open in the editor, this is working as intended
180// If the file is not open in the editor, the errors go away
181const test38878 = `
182-- go.mod --
183module foo
184
185go 1.12
186-- a.go --
187package x
188
189// import "fmt"
190
191func f() {}
192
193-- a_test.go --
194package x
195
196import "testing"
197
198func TestA(t *testing.T) {
199	f(3)
200}
201`
202
203// Tests golang/go#38878: deleting a test file should clear its errors, and
204// not break the workspace.
205func TestDeleteTestVariant(t *testing.T) {
206	runner.Run(t, test38878, func(t *testing.T, env *Env) {
207		env.Await(env.DiagnosticAtRegexp("a_test.go", `f\((3)\)`))
208		env.RemoveWorkspaceFile("a_test.go")
209		env.Await(EmptyDiagnostics("a_test.go"))
210
211		// Make sure the test variant has been removed from the workspace by
212		// triggering a metadata load.
213		env.OpenFile("a.go")
214		env.RegexpReplace("a.go", `// import`, "import")
215		env.Await(env.DiagnosticAtRegexp("a.go", `"fmt"`))
216	})
217}
218
219// Tests golang/go#38878: deleting a test file on disk while it's still open
220// should not clear its errors.
221func TestDeleteTestVariant_DiskOnly(t *testing.T) {
222	log.SetFlags(log.Lshortfile)
223	runner.Run(t, test38878, func(t *testing.T, env *Env) {
224		env.OpenFile("a_test.go")
225		env.Await(DiagnosticAt("a_test.go", 5, 3))
226		env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go")
227		env.Await(OnceMet(
228			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
229			DiagnosticAt("a_test.go", 5, 3)))
230	})
231}
232
233// TestNoMod confirms that gopls continues to work when a user adds a go.mod
234// file to their workspace.
235func TestNoMod(t *testing.T) {
236	const noMod = `
237-- main.go --
238package main
239
240import "mod.com/bob"
241
242func main() {
243	bob.Hello()
244}
245-- bob/bob.go --
246package bob
247
248func Hello() {
249	var x int
250}
251`
252
253	t.Run("manual", func(t *testing.T) {
254		runner.Run(t, noMod, func(t *testing.T, env *Env) {
255			env.Await(
256				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
257			)
258			env.CreateBuffer("go.mod", `module mod.com
259
260	go 1.12
261`)
262			env.SaveBuffer("go.mod")
263			env.Await(
264				EmptyDiagnostics("main.go"),
265			)
266			var d protocol.PublishDiagnosticsParams
267			env.Await(
268				OnceMet(
269					env.DiagnosticAtRegexp("bob/bob.go", "x"),
270					ReadDiagnostics("bob/bob.go", &d),
271				),
272			)
273			if len(d.Diagnostics) != 1 {
274				t.Fatalf("expected 1 diagnostic, got %v", len(d.Diagnostics))
275			}
276		})
277	})
278	t.Run("initialized", func(t *testing.T) {
279		runner.Run(t, noMod, func(t *testing.T, env *Env) {
280			env.Await(
281				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
282			)
283			env.RunGoCommand("mod", "init", "mod.com")
284			env.Await(
285				EmptyDiagnostics("main.go"),
286				env.DiagnosticAtRegexp("bob/bob.go", "x"),
287			)
288		})
289	})
290
291	t.Run("without workspace module", func(t *testing.T) {
292		withOptions(
293			Modes(Singleton),
294		).run(t, noMod, func(t *testing.T, env *Env) {
295			env.Await(
296				env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
297			)
298			if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}); err != nil {
299				t.Fatal(err)
300			}
301			env.Await(
302				EmptyDiagnostics("main.go"),
303				env.DiagnosticAtRegexp("bob/bob.go", "x"),
304			)
305		})
306	})
307}
308
309// Tests golang/go#38267.
310func TestIssue38267(t *testing.T) {
311	const testPackage = `
312-- go.mod --
313module mod.com
314
315go 1.12
316-- lib.go --
317package lib
318
319func Hello(x string) {
320	_ = x
321}
322-- lib_test.go --
323package lib
324
325import "testing"
326
327type testStruct struct{
328	name string
329}
330
331func TestHello(t *testing.T) {
332	testStructs := []*testStruct{
333		&testStruct{"hello"},
334		&testStruct{"goodbye"},
335	}
336	for y := range testStructs {
337		_ = y
338	}
339}
340`
341
342	runner.Run(t, testPackage, func(t *testing.T, env *Env) {
343		env.OpenFile("lib_test.go")
344		env.Await(
345			DiagnosticAt("lib_test.go", 10, 2),
346			DiagnosticAt("lib_test.go", 11, 2),
347		)
348		env.OpenFile("lib.go")
349		env.RegexpReplace("lib.go", "_ = x", "var y int")
350		env.Await(
351			env.DiagnosticAtRegexp("lib.go", "y int"),
352			EmptyDiagnostics("lib_test.go"),
353		)
354	})
355}
356
357// Tests golang/go#38328.
358func TestPackageChange_Issue38328(t *testing.T) {
359	const packageChange = `
360-- go.mod --
361module fake
362
363go 1.12
364-- a.go --
365package foo
366func main() {}
367`
368	runner.Run(t, packageChange, func(t *testing.T, env *Env) {
369		env.OpenFile("a.go")
370		env.RegexpReplace("a.go", "foo", "foox")
371		env.Await(
372			// When the bug reported in #38328 was present, we didn't get erroneous
373			// file diagnostics until after the didChange message generated by the
374			// package renaming was fully processed. Therefore, in order for this
375			// test to actually exercise the bug, we must wait until that work has
376			// completed.
377			OnceMet(
378				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
379				NoDiagnostics("a.go"),
380			),
381		)
382	})
383}
384
385const testPackageWithRequire = `
386-- go.mod --
387module mod.com
388
389go 1.12
390
391require foo.test v1.2.3
392-- go.sum --
393foo.test v1.2.3 h1:TMA+lyd1ck0TqjSFpNe4T6cf/K6TYkoHwOOcMBMjaEw=
394foo.test v1.2.3/go.mod h1:Ij3kyLIe5lzjycjh13NL8I2gX0quZuTdW0MnmlwGBL4=
395-- print.go --
396package lib
397
398import (
399	"fmt"
400
401	"foo.test/bar"
402)
403
404func PrintAnswer() {
405	fmt.Printf("answer: %s", bar.Answer)
406}
407`
408
409const testPackageWithRequireProxy = `
410-- foo.test@v1.2.3/go.mod --
411module foo.test
412
413go 1.12
414-- foo.test@v1.2.3/bar/const.go --
415package bar
416
417const Answer = 42
418`
419
420func TestResolveDiagnosticWithDownload(t *testing.T) {
421	runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
422		env.OpenFile("print.go")
423		// Check that gopackages correctly loaded this dependency. We should get a
424		// diagnostic for the wrong formatting type.
425		// TODO: we should be able to easily also match the diagnostic message.
426		env.Await(env.DiagnosticAtRegexp("print.go", "fmt.Printf"))
427	}, ProxyFiles(testPackageWithRequireProxy))
428}
429
430func TestMissingDependency(t *testing.T) {
431	runner.Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
432		env.OpenFile("print.go")
433		env.Await(LogMatching(protocol.Error, "initial workspace load failed", 1))
434	})
435}
436
437// Tests golang/go#36951.
438func TestAdHocPackages_Issue36951(t *testing.T) {
439	const adHoc = `
440-- b/b.go --
441package b
442
443func Hello() {
444	var x int
445}
446`
447	runner.Run(t, adHoc, func(t *testing.T, env *Env) {
448		env.OpenFile("b/b.go")
449		env.Await(env.DiagnosticAtRegexp("b/b.go", "x"))
450	})
451}
452
453// Tests golang/go#37984: GOPATH should be read from the go command.
454func TestNoGOPATH_Issue37984(t *testing.T) {
455	const files = `
456-- main.go --
457package main
458
459func _() {
460	fmt.Println("Hello World")
461}
462`
463	withOptions(
464		EditorConfig{
465			Env: map[string]string{
466				"GOPATH":      "",
467				"GO111MODULE": "off",
468			},
469		}).run(t, files, func(t *testing.T, env *Env) {
470		env.OpenFile("main.go")
471		env.Await(env.DiagnosticAtRegexp("main.go", "fmt"))
472		env.SaveBuffer("main.go")
473		env.Await(EmptyDiagnostics("main.go"))
474	})
475}
476
477// Tests golang/go#38669.
478func TestEqualInEnv_Issue38669(t *testing.T) {
479	const files = `
480-- go.mod --
481module mod.com
482
483go 1.12
484-- main.go --
485package main
486
487var _ = x.X
488-- x/x.go --
489package x
490
491var X = 0
492`
493	editorConfig := EditorConfig{Env: map[string]string{"GOFLAGS": "-tags=foo"}}
494	withOptions(editorConfig).run(t, files, func(t *testing.T, env *Env) {
495		env.OpenFile("main.go")
496		env.OrganizeImports("main.go")
497		env.Await(EmptyDiagnostics("main.go"))
498	})
499}
500
501// Tests golang/go#38467.
502func TestNoSuggestedFixesForGeneratedFiles_Issue38467(t *testing.T) {
503	const generated = `
504-- go.mod --
505module mod.com
506
507go 1.12
508-- main.go --
509package main
510
511// Code generated by generator.go. DO NOT EDIT.
512
513func _() {
514	for i, _ := range []string{} {
515		_ = i
516	}
517}
518`
519	runner.Run(t, generated, func(t *testing.T, env *Env) {
520		env.OpenFile("main.go")
521		original := env.ReadWorkspaceFile("main.go")
522		var d protocol.PublishDiagnosticsParams
523		env.Await(
524			OnceMet(
525				DiagnosticAt("main.go", 5, 8),
526				ReadDiagnostics("main.go", &d),
527			),
528		)
529		// Apply fixes and save the buffer.
530		env.ApplyQuickFixes("main.go", d.Diagnostics)
531		env.SaveBuffer("main.go")
532		fixed := env.ReadWorkspaceFile("main.go")
533		if original != fixed {
534			t.Fatalf("generated file was changed by quick fixes:\n%s", tests.Diff(t, original, fixed))
535		}
536	})
537}
538
539// Expect a module/GOPATH error if there is an error in the file at startup.
540// Tests golang/go#37279.
541func TestShowCriticalError_Issue37279(t *testing.T) {
542	const noModule = `
543-- a.go --
544package foo
545
546import "mod.com/hello"
547
548func f() {
549	hello.Goodbye()
550}
551`
552	runner.Run(t, noModule, func(t *testing.T, env *Env) {
553		env.OpenFile("a.go")
554		env.Await(
555			OutstandingWork(lsp.WorkspaceLoadFailure, "outside of a module"),
556		)
557		env.RegexpReplace("a.go", `import "mod.com/hello"`, "")
558		env.Await(
559			NoOutstandingWork(),
560		)
561	})
562}
563
564func TestNonGoFolder(t *testing.T) {
565	const files = `
566-- hello.txt --
567hi mom
568`
569	for _, go111module := range []string{"on", "off", ""} {
570		t.Run(fmt.Sprintf("GO111MODULE_%v", go111module), func(t *testing.T) {
571			withOptions(EditorConfig{
572				Env: map[string]string{"GO111MODULE": go111module},
573			}).run(t, files, func(t *testing.T, env *Env) {
574				env.OpenFile("hello.txt")
575				env.Await(
576					OnceMet(
577						CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
578						NoShowMessage(),
579					),
580				)
581			})
582		})
583	}
584}
585
586// Tests golang/go#38602.
587func TestNonexistentFileDiagnostics_Issue38602(t *testing.T) {
588	const collision = `
589-- x/x.go --
590package x
591
592import "x/hello"
593
594func Hello() {
595	hello.HiThere()
596}
597-- x/main.go --
598package main
599
600func main() {
601	fmt.Println("")
602}
603`
604	runner.Run(t, collision, func(t *testing.T, env *Env) {
605		env.OpenFile("x/main.go")
606		env.Await(
607			env.DiagnosticAtRegexp("x/main.go", "fmt.Println"),
608		)
609		env.OrganizeImports("x/main.go")
610		// span.Parse misparses the error message when multiple packages are
611		// defined in the same directory, creating a garbage filename.
612		// Previously, we would send diagnostics for this nonexistent file.
613		// This test checks that we don't send diagnostics for this file.
614		dir, err := os.Getwd()
615		if err != nil {
616			t.Fatal(err)
617		}
618		badFile := fmt.Sprintf("%s/found packages main (main.go) and x (x.go) in %s/src/x", dir, env.Sandbox.GOPATH())
619		env.Await(
620			OnceMet(
621				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
622				EmptyDiagnostics("x/main.go"),
623			),
624			NoDiagnostics(badFile),
625		)
626	}, InGOPATH())
627}
628
629const ardanLabsProxy = `
630-- github.com/ardanlabs/conf@v1.2.3/go.mod --
631module github.com/ardanlabs/conf
632
633go 1.12
634-- github.com/ardanlabs/conf@v1.2.3/conf.go --
635package conf
636
637var ErrHelpWanted error
638`
639
640// Test for golang/go#38211.
641func Test_Issue38211(t *testing.T) {
642	testenv.NeedsGo1Point(t, 14)
643	const ardanLabs = `
644-- go.mod --
645module mod.com
646
647go 1.14
648-- main.go --
649package main
650
651import "github.com/ardanlabs/conf"
652
653func main() {
654	_ = conf.ErrHelpWanted
655}
656`
657	withOptions(
658		ProxyFiles(ardanLabsProxy),
659	).run(t, ardanLabs, func(t *testing.T, env *Env) {
660		// Expect a diagnostic with a suggested fix to add
661		// "github.com/ardanlabs/conf" to the go.mod file.
662		env.OpenFile("go.mod")
663		env.OpenFile("main.go")
664		var d protocol.PublishDiagnosticsParams
665		env.Await(
666			OnceMet(
667				env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
668				ReadDiagnostics("main.go", &d),
669			),
670		)
671		env.ApplyQuickFixes("main.go", d.Diagnostics)
672		env.SaveBuffer("go.mod")
673		env.Await(
674			EmptyDiagnostics("main.go"),
675		)
676		// Comment out the line that depends on conf and expect a
677		// diagnostic and a fix to remove the import.
678		env.RegexpReplace("main.go", "_ = conf.ErrHelpWanted", "//_ = conf.ErrHelpWanted")
679		env.Await(
680			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
681		)
682		env.SaveBuffer("main.go")
683		// Expect a diagnostic and fix to remove the dependency in the go.mod.
684		env.Await(EmptyDiagnostics("main.go"))
685		env.Await(
686			OnceMet(
687				env.DiagnosticAtRegexp("go.mod", "require github.com/ardanlabs/conf"),
688				ReadDiagnostics("go.mod", &d),
689			),
690		)
691		env.ApplyQuickFixes("go.mod", d.Diagnostics)
692		env.SaveBuffer("go.mod")
693		env.Await(
694			EmptyDiagnostics("go.mod"),
695		)
696		// Uncomment the lines and expect a new diagnostic for the import.
697		env.RegexpReplace("main.go", "//_ = conf.ErrHelpWanted", "_ = conf.ErrHelpWanted")
698		env.SaveBuffer("main.go")
699		env.Await(
700			env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
701		)
702	})
703}
704
705// Test for golang/go#38207.
706func TestNewModule_Issue38207(t *testing.T) {
707	testenv.NeedsGo1Point(t, 14)
708	const emptyFile = `
709-- go.mod --
710module mod.com
711
712go 1.12
713-- main.go --
714`
715	withOptions(
716		ProxyFiles(ardanLabsProxy),
717	).run(t, emptyFile, func(t *testing.T, env *Env) {
718		env.OpenFile("main.go")
719		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
720
721import "github.com/ardanlabs/conf"
722
723func main() {
724	_ = conf.ErrHelpWanted
725}
726`))
727		env.SaveBuffer("main.go")
728		var d protocol.PublishDiagnosticsParams
729		env.Await(
730			OnceMet(
731				env.DiagnosticAtRegexpWithMessage("main.go", `"github.com/ardanlabs/conf"`, "your go.mod file"),
732				ReadDiagnostics("main.go", &d),
733			),
734		)
735		env.ApplyQuickFixes("main.go", d.Diagnostics)
736		env.CheckForFileChanges()
737		env.Await(
738			EmptyDiagnostics("main.go"),
739		)
740	})
741}
742
743// Test for golang/go#36960.
744func TestNewFileBadImports_Issue36960(t *testing.T) {
745	testenv.NeedsGo1Point(t, 14)
746	const simplePackage = `
747-- go.mod --
748module mod.com
749
750go 1.14
751-- a/a1.go --
752package a
753
754import "fmt"
755
756func _() {
757	fmt.Println("hi")
758}
759`
760	runner.Run(t, simplePackage, func(t *testing.T, env *Env) {
761		env.OpenFile("a/a1.go")
762		env.CreateBuffer("a/a2.go", ``)
763		env.SaveBufferWithoutActions("a/a2.go")
764		env.Await(
765			OnceMet(
766				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
767				NoDiagnostics("a/a1.go"),
768			),
769		)
770		env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`))
771		env.Await(
772			OnceMet(
773				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
774				NoDiagnostics("a/a1.go"),
775			),
776		)
777	})
778}
779
780// This test tries to replicate the workflow of a user creating a new x test.
781// It also tests golang/go#39315.
782func TestManuallyCreatingXTest(t *testing.T) {
783	// Only for 1.15 because of golang/go#37971.
784	testenv.NeedsGo1Point(t, 15)
785
786	// Create a package that already has a test variant (in-package test).
787	const testVariant = `
788-- go.mod --
789module mod.com
790
791go 1.15
792-- hello/hello.go --
793package hello
794
795func Hello() {
796	var x int
797}
798-- hello/hello_test.go --
799package hello
800
801import "testing"
802
803func TestHello(t *testing.T) {
804	var x int
805	Hello()
806}
807`
808	runner.Run(t, testVariant, func(t *testing.T, env *Env) {
809		// Open the file, triggering the workspace load.
810		// There are errors in the code to ensure all is working as expected.
811		env.OpenFile("hello/hello.go")
812		env.Await(
813			env.DiagnosticAtRegexp("hello/hello.go", "x"),
814			env.DiagnosticAtRegexp("hello/hello_test.go", "x"),
815		)
816
817		// Create an empty file with the intention of making it an x test.
818		// This resembles a typical flow in an editor like VS Code, in which
819		// a user would create an empty file and add content, saving
820		// intermittently.
821		// TODO(rstambler): There might be more edge cases here, as file
822		// content can be added incrementally.
823		env.CreateBuffer("hello/hello_x_test.go", ``)
824
825		// Save the empty file (no actions since formatting will fail).
826		env.SaveBufferWithoutActions("hello/hello_x_test.go")
827
828		// Add the content. The missing import is for the package under test.
829		env.EditBuffer("hello/hello_x_test.go", fake.NewEdit(0, 0, 0, 0, `package hello_test
830
831import (
832	"testing"
833)
834
835func TestHello(t *testing.T) {
836	hello.Hello()
837}
838`))
839		// Expect a diagnostic for the missing import. Save, which should
840		// trigger import organization. The diagnostic should clear.
841		env.Await(
842			env.DiagnosticAtRegexp("hello/hello_x_test.go", "hello.Hello"),
843		)
844		env.SaveBuffer("hello/hello_x_test.go")
845		env.Await(
846			EmptyDiagnostics("hello/hello_x_test.go"),
847		)
848	})
849}
850
851// Reproduce golang/go#40690.
852func TestCreateOnlyXTest(t *testing.T) {
853	testenv.NeedsGo1Point(t, 13)
854
855	const mod = `
856-- go.mod --
857module mod.com
858
859go 1.12
860-- foo/foo.go --
861package foo
862-- foo/bar_test.go --
863`
864	run(t, mod, func(t *testing.T, env *Env) {
865		env.OpenFile("foo/bar_test.go")
866		env.EditBuffer("foo/bar_test.go", fake.NewEdit(0, 0, 0, 0, "package foo"))
867		env.Await(
868			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
869		)
870		env.RegexpReplace("foo/bar_test.go", "package foo", `package foo_test
871
872import "testing"
873
874func TestX(t *testing.T) {
875	var x int
876}
877`)
878		env.Await(
879			env.DiagnosticAtRegexp("foo/bar_test.go", "x"),
880		)
881	})
882}
883
884func TestChangePackageName(t *testing.T) {
885	t.Skip("This issue hasn't been fixed yet. See golang.org/issue/41061.")
886
887	const mod = `
888-- go.mod --
889module mod.com
890
891go 1.12
892-- foo/foo.go --
893package foo
894-- foo/bar_test.go --
895package foo_
896`
897	run(t, mod, func(t *testing.T, env *Env) {
898		env.OpenFile("foo/bar_test.go")
899		env.RegexpReplace("foo/bar_test.go", "package foo_", "package foo_test")
900		env.SaveBuffer("foo/bar_test.go")
901		env.Await(
902			OnceMet(
903				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
904				NoDiagnostics("foo/bar_test.go"),
905			),
906			OnceMet(
907				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
908				NoDiagnostics("foo/foo.go"),
909			),
910		)
911	})
912}
913
914// Reproduces golang/go#40825.
915func TestEmptyGOPATHXTest_40825(t *testing.T) {
916	const files = `
917-- x.go --
918package x
919-- x_test.go --
920`
921
922	withOptions(InGOPATH()).run(t, files, func(t *testing.T, env *Env) {
923		env.OpenFile("x_test.go")
924		env.EditBuffer("x_test.go", fake.NewEdit(0, 0, 0, 0, "pack"))
925		env.Await(
926			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
927			NoShowMessage(),
928		)
929	})
930}
931
932func TestIgnoredFiles(t *testing.T) {
933	const ws = `
934-- go.mod --
935module mod.com
936
937go 1.12
938-- _foo/x.go --
939package x
940
941var _ = foo.Bar
942`
943	runner.Run(t, ws, func(t *testing.T, env *Env) {
944		env.OpenFile("_foo/x.go")
945		env.Await(
946			OnceMet(
947				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
948				NoDiagnostics("_foo/x.go"),
949			))
950	})
951}
952
953// Partially reproduces golang/go#38977, moving a file between packages.
954// It also gets hit by some go command bug fixed in 1.15, but we don't
955// care about that so much here.
956func TestDeletePackage(t *testing.T) {
957	const ws = `
958-- go.mod --
959module mod.com
960
961go 1.15
962-- a/a.go --
963package a
964
965const A = 1
966
967-- b/b.go --
968package b
969
970import "mod.com/a"
971
972const B = a.A
973
974-- c/c.go --
975package c
976
977import "mod.com/a"
978
979const C = a.A
980`
981	runner.Run(t, ws, func(t *testing.T, env *Env) {
982		env.OpenFile("b/b.go")
983		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
984		// Delete c/c.go, the only file in package c.
985		env.RemoveWorkspaceFile("c/c.go")
986
987		// We should still get diagnostics for files that exist.
988		env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant")
989		env.Await(env.DiagnosticAtRegexp("b/b.go", `Nonexistant`))
990	})
991}
992
993// This is a copy of the scenario_default/quickfix_empty_files.txt test from
994// govim. Reproduces golang/go#39646.
995func TestQuickFixEmptyFiles(t *testing.T) {
996	testenv.NeedsGo1Point(t, 15)
997
998	const mod = `
999-- go.mod --
1000module mod.com
1001
1002go 1.12
1003`
1004	// To fully recreate the govim tests, we create files by inserting
1005	// a newline, adding to the file, and then deleting the newline.
1006	// Wait for each event to process to avoid cancellations and force
1007	// package loads.
1008	writeGoVim := func(env *Env, name, content string) {
1009		env.WriteWorkspaceFile(name, "")
1010		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1))
1011
1012		env.CreateBuffer(name, "\n")
1013		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
1014
1015		env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content))
1016		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1))
1017
1018		env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, ""))
1019		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1))
1020	}
1021
1022	const p = `package p; func DoIt(s string) {};`
1023	const main = `package main
1024
1025import "mod.com/p"
1026
1027func main() {
1028	p.DoIt(5)
1029}
1030`
1031	// A simple version of the test that reproduces most of the problems it
1032	// exposes.
1033	t.Run("short", func(t *testing.T) {
1034		runner.Run(t, mod, func(t *testing.T, env *Env) {
1035			writeGoVim(env, "p/p.go", p)
1036			writeGoVim(env, "main.go", main)
1037			env.Await(env.DiagnosticAtRegexp("main.go", "5"))
1038		})
1039	})
1040
1041	// A full version that replicates the whole flow of the test.
1042	t.Run("full", func(t *testing.T) {
1043		runner.Run(t, mod, func(t *testing.T, env *Env) {
1044			writeGoVim(env, "p/p.go", p)
1045			writeGoVim(env, "main.go", main)
1046			writeGoVim(env, "p/p_test.go", `package p
1047
1048import "testing"
1049
1050func TestDoIt(t *testing.T) {
1051	DoIt(5)
1052}
1053`)
1054			writeGoVim(env, "p/x_test.go", `package p_test
1055
1056import (
1057	"testing"
1058
1059	"mod.com/p"
1060)
1061
1062func TestDoIt(t *testing.T) {
1063	p.DoIt(5)
1064}
1065`)
1066			env.Await(
1067				env.DiagnosticAtRegexp("main.go", "5"),
1068				env.DiagnosticAtRegexp("p/p_test.go", "5"),
1069				env.DiagnosticAtRegexp("p/x_test.go", "5"),
1070			)
1071			env.RegexpReplace("p/p.go", "s string", "i int")
1072			env.Await(
1073				EmptyDiagnostics("main.go"),
1074				EmptyDiagnostics("p/p_test.go"),
1075				EmptyDiagnostics("p/x_test.go"),
1076			)
1077		})
1078	})
1079}
1080
1081func TestSingleFile(t *testing.T) {
1082	const mod = `
1083-- go.mod --
1084module mod.com
1085
1086go 1.13
1087-- a/a.go --
1088package a
1089
1090func _() {
1091	var x int
1092}
1093`
1094	withOptions(
1095		// Empty workspace folders.
1096		WorkspaceFolders(),
1097	).run(t, mod, func(t *testing.T, env *Env) {
1098		env.OpenFile("a/a.go")
1099		env.Await(
1100			env.DiagnosticAtRegexp("a/a.go", "x"),
1101		)
1102	})
1103}
1104
1105// Reproduces the case described in
1106// https://github.com/golang/go/issues/39296#issuecomment-652058883.
1107func TestPkgm(t *testing.T) {
1108	const basic = `
1109-- go.mod --
1110module mod.com
1111
1112go 1.15
1113-- foo/foo.go --
1114package foo
1115
1116import "fmt"
1117
1118func Foo() {
1119	fmt.Println("")
1120}
1121`
1122	runner.Run(t, basic, func(t *testing.T, env *Env) {
1123		testenv.NeedsGo1Point(t, 15)
1124
1125		env.WriteWorkspaceFile("foo/foo_test.go", `package main
1126
1127func main() {
1128
1129}`)
1130		env.OpenFile("foo/foo_test.go")
1131		env.RegexpReplace("foo/foo_test.go", `package main`, `package foo`)
1132		env.Await(
1133			OnceMet(
1134				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
1135				NoDiagnostics("foo/foo.go"),
1136			),
1137		)
1138	})
1139}
1140
1141func TestClosingBuffer(t *testing.T) {
1142	const basic = `
1143-- go.mod --
1144module mod.com
1145
1146go 1.14
1147-- main.go --
1148package main
1149
1150func main() {}
1151`
1152	runner.Run(t, basic, func(t *testing.T, env *Env) {
1153		env.Editor.CreateBuffer(env.Ctx, "foo.go", `package main`)
1154		env.Await(
1155			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
1156		)
1157		env.CloseBuffer("foo.go")
1158		env.Await(
1159			OnceMet(
1160				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidClose), 1),
1161				NoLogMatching(protocol.Info, "packages=0"),
1162			),
1163		)
1164	})
1165}
1166
1167// Reproduces golang/go#38424.
1168func TestCutAndPaste(t *testing.T) {
1169	const basic = `
1170-- go.mod --
1171module mod.com
1172
1173go 1.14
1174-- main2.go --
1175package main
1176`
1177	runner.Run(t, basic, func(t *testing.T, env *Env) {
1178		env.CreateBuffer("main.go", "")
1179		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
1180
1181		env.SaveBufferWithoutActions("main.go")
1182		env.Await(
1183			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 1),
1184			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
1185		)
1186
1187		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
1188
1189func main() {
1190}
1191`))
1192		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1))
1193
1194		env.SaveBuffer("main.go")
1195		env.Await(
1196			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), 2),
1197			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 2),
1198		)
1199
1200		env.EditBuffer("main.go", fake.NewEdit(0, 0, 4, 0, ""))
1201		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 2))
1202
1203		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
1204
1205func main() {
1206	var x int
1207}
1208`))
1209		env.Await(
1210			env.DiagnosticAtRegexp("main.go", "x"),
1211		)
1212	})
1213}
1214
1215// Reproduces golang/go#39763.
1216func TestInvalidPackageName(t *testing.T) {
1217	testenv.NeedsGo1Point(t, 15)
1218
1219	const pkgDefault = `
1220-- go.mod --
1221module mod.com
1222
1223go 1.12
1224-- main.go --
1225package default
1226
1227func main() {}
1228`
1229	runner.Run(t, pkgDefault, func(t *testing.T, env *Env) {
1230		env.OpenFile("main.go")
1231		env.Await(
1232			env.DiagnosticAtRegexp("main.go", "default"),
1233		)
1234	})
1235}
1236
1237// This tests the functionality of the "limitWorkspaceScope"
1238func TestLimitWorkspaceScope(t *testing.T) {
1239	const mod = `
1240-- go.mod --
1241module mod.com
1242
1243go 1.12
1244-- a/main.go --
1245package main
1246
1247func main() {}
1248-- main.go --
1249package main
1250
1251func main() {
1252	var x int
1253}
1254`
1255	withOptions(
1256		WorkspaceFolders("a"),
1257	).run(t, mod, func(t *testing.T, env *Env) {
1258		env.OpenFile("a/main.go")
1259		env.Await(
1260			env.DiagnosticAtRegexp("main.go", "x"),
1261		)
1262	})
1263	withOptions(
1264		WorkspaceFolders("a"),
1265		LimitWorkspaceScope(),
1266	).run(t, mod, func(t *testing.T, env *Env) {
1267		env.OpenFile("a/main.go")
1268		env.Await(
1269			NoDiagnostics("main.go"),
1270		)
1271	})
1272}
1273
1274func TestStaticcheckDiagnostic(t *testing.T) {
1275	const files = `
1276-- go.mod --
1277module mod.com
1278
1279go 1.12
1280-- main.go --
1281package main
1282
1283import "fmt"
1284
1285type t struct {
1286	msg string
1287}
1288
1289func main() {
1290	x := []t{t{"msg"}}
1291	fmt.Println(x)
1292}
1293`
1294
1295	withOptions(
1296		EditorConfig{EnableStaticcheck: true},
1297	).run(t, files, func(t *testing.T, env *Env) {
1298		env.OpenFile("main.go")
1299		// Staticcheck should generate a diagnostic to simplify this literal.
1300		env.Await(env.DiagnosticAtRegexp("main.go", `t{"msg"}`))
1301	})
1302}
1303
1304// Test some secondary diagnostics
1305func TestSecondaryDiagnostics(t *testing.T) {
1306	const dir = `
1307-- go.mod --
1308module mod.com
1309
1310go 1.12
1311-- main.go --
1312package main
1313func main() {
1314	panic("not here")
1315}
1316-- other.go --
1317package main
1318func main() {}
1319`
1320	runner.Run(t, dir, func(t *testing.T, env *Env) {
1321		log.SetFlags(log.Lshortfile)
1322		env.OpenFile("main.go")
1323		env.OpenFile("other.go")
1324		x := env.DiagnosticsFor("main.go")
1325		if x == nil {
1326			t.Fatalf("expected 1 diagnostic, got none")
1327		}
1328		if len(x.Diagnostics) != 1 {
1329			t.Fatalf("main.go, got %d diagnostics, expected 1", len(x.Diagnostics))
1330		}
1331		keep := x.Diagnostics[0]
1332		y := env.DiagnosticsFor("other.go")
1333		if len(y.Diagnostics) != 1 {
1334			t.Fatalf("other.go: got %d diagnostics, expected 1", len(y.Diagnostics))
1335		}
1336		if len(y.Diagnostics[0].RelatedInformation) != 1 {
1337			t.Fatalf("got %d RelatedInformations, expected 1", len(y.Diagnostics[0].RelatedInformation))
1338		}
1339		// check that the RelatedInformation matches the error from main.go
1340		c := y.Diagnostics[0].RelatedInformation[0]
1341		if c.Location.Range != keep.Range {
1342			t.Errorf("locations don't match. Got %v expected %v", c.Location.Range, keep.Range)
1343		}
1344	})
1345}
1346
1347func TestNotifyOrphanedFiles(t *testing.T) {
1348	// Need GO111MODULE=on for this test to work with Go 1.12.
1349	testenv.NeedsGo1Point(t, 13)
1350
1351	const files = `
1352-- go.mod --
1353module mod.com
1354
1355go 1.12
1356-- a/a.go --
1357package a
1358
1359func main() {
1360	var x int
1361}
1362-- a/a_ignore.go --
1363// +build ignore
1364
1365package a
1366
1367func _() {
1368	var x int
1369}
1370`
1371	run(t, files, func(t *testing.T, env *Env) {
1372		env.OpenFile("a/a.go")
1373		env.Await(
1374			env.DiagnosticAtRegexp("a/a.go", "x"),
1375		)
1376		env.OpenFile("a/a_ignore.go")
1377		env.Await(
1378			DiagnosticAt("a/a_ignore.go", 2, 8),
1379		)
1380	})
1381}
1382
1383func TestEnableAllExperiments(t *testing.T) {
1384	const mod = `
1385-- go.mod --
1386module mod.com
1387
1388go 1.12
1389-- main.go --
1390package main
1391
1392import "bytes"
1393
1394func b(c bytes.Buffer) {
1395	_ = 1
1396}
1397`
1398	withOptions(
1399		EditorConfig{
1400			AllExperiments: true,
1401		},
1402	).run(t, mod, func(t *testing.T, env *Env) {
1403		// Confirm that the setting doesn't cause any warnings.
1404		env.Await(NoShowMessage())
1405	})
1406}
1407
1408func TestSwig(t *testing.T) {
1409	t.Skipf("skipped until golang/go#37098 is resolved")
1410
1411	const mod = `
1412-- go.mod --
1413module mod.com
1414
1415go 1.12
1416-- pkg/simple/export_swig.go --
1417package simple
1418
1419func ExportSimple(x, y int) int {
1420	return Gcd(x, y)
1421}
1422-- pkg/simple/simple.swigcxx --
1423%module simple
1424
1425%inline %{
1426extern int gcd(int x, int y)
1427{
1428  int g;
1429  g = y;
1430  while (x > 0) {
1431    g = x;
1432    x = y % x;
1433    y = g;
1434  }
1435  return g;
1436}
1437%}
1438-- main.go --
1439package a
1440
1441func main() {
1442	var x int
1443}
1444`
1445	run(t, mod, func(t *testing.T, env *Env) {
1446		env.Await(
1447			OnceMet(
1448				InitialWorkspaceLoad,
1449				NoDiagnosticWithMessage("", "illegal character U+0023 '#'"),
1450			),
1451		)
1452	})
1453}
1454
1455// When foo_test.go is opened, gopls will object to the borked package name.
1456// This test asserts that when the package name is fixed, gopls will soon after
1457// have no more complaints about it.
1458// https://github.com/golang/go/issues/41061
1459func TestRenamePackage(t *testing.T) {
1460	testenv.NeedsGo1Point(t, 16)
1461
1462	const contents = `
1463-- go.mod --
1464module mod.com
1465
1466go 1.12
1467-- main.go --
1468package main
1469
1470import "example.com/blah"
1471
1472func main() {
1473	blah.Hello()
1474}
1475-- bob.go --
1476package main
1477-- foo/foo.go --
1478package foo
1479-- foo/foo_test.go --
1480package foo_
1481`
1482
1483	withOptions(
1484		ProxyFiles(proxy),
1485		InGOPATH(),
1486	).run(t, contents, func(t *testing.T, env *Env) {
1487		// Simulate typing character by character.
1488		env.OpenFile("foo/foo_test.go")
1489		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
1490		env.RegexpReplace("foo/foo_test.go", "_", "_t")
1491		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1))
1492		env.RegexpReplace("foo/foo_test.go", "_t", "_test")
1493		env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 2))
1494
1495		env.Await(
1496			EmptyDiagnostics("foo/foo_test.go"),
1497			NoOutstandingWork(),
1498		)
1499	})
1500}
1501
1502// TestProgressBarErrors confirms that critical workspace load errors are shown
1503// and updated via progress reports.
1504func TestProgressBarErrors(t *testing.T) {
1505	testenv.NeedsGo1Point(t, 14)
1506
1507	const pkg = `
1508-- go.mod --
1509modul mod.com
1510
1511go 1.12
1512-- main.go --
1513package main
1514`
1515	run(t, pkg, func(t *testing.T, env *Env) {
1516		env.OpenFile("go.mod")
1517		env.Await(
1518			OutstandingWork(lsp.WorkspaceLoadFailure, "unknown directive"),
1519		)
1520		env.EditBuffer("go.mod", fake.NewEdit(0, 0, 3, 0, `module mod.com
1521
1522go 1.hello
1523`))
1524		// As of golang/go#42529, go.mod changes do not reload the workspace until
1525		// they are saved.
1526		env.SaveBufferWithoutActions("go.mod")
1527		env.Await(
1528			OutstandingWork(lsp.WorkspaceLoadFailure, "invalid go version"),
1529		)
1530		env.RegexpReplace("go.mod", "go 1.hello", "go 1.12")
1531		env.SaveBufferWithoutActions("go.mod")
1532		env.Await(
1533			NoOutstandingWork(),
1534		)
1535	})
1536}
1537
1538func TestDeleteDirectory(t *testing.T) {
1539	testenv.NeedsGo1Point(t, 14)
1540
1541	const mod = `
1542-- bob/bob.go --
1543package bob
1544
1545func Hello() {
1546	var x int
1547}
1548-- go.mod --
1549module mod.com
1550-- main.go --
1551package main
1552
1553import "mod.com/bob"
1554
1555func main() {
1556	bob.Hello()
1557}
1558`
1559	run(t, mod, func(t *testing.T, env *Env) {
1560		env.RemoveWorkspaceFile("bob")
1561		env.Await(
1562			env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
1563			EmptyDiagnostics("bob/bob.go"),
1564		)
1565	})
1566}
1567
1568// Confirms that circular imports are tested and reported.
1569func TestCircularImports(t *testing.T) {
1570	const mod = `
1571-- go.mod --
1572module mod.com
1573
1574go 1.12
1575-- self/self.go --
1576package self
1577
1578import _ "mod.com/self"
1579func Hello() {}
1580-- double/a/a.go --
1581package a
1582
1583import _ "mod.com/double/b"
1584-- double/b/b.go --
1585package b
1586
1587import _ "mod.com/double/a"
1588-- triple/a/a.go --
1589package a
1590
1591import _ "mod.com/triple/b"
1592-- triple/b/b.go --
1593package b
1594
1595import _ "mod.com/triple/c"
1596-- triple/c/c.go --
1597package c
1598
1599import _ "mod.com/triple/a"
1600`
1601	run(t, mod, func(t *testing.T, env *Env) {
1602		env.Await(
1603			env.DiagnosticAtRegexpWithMessage("self/self.go", `_ "mod.com/self"`, "import cycle not allowed"),
1604			env.DiagnosticAtRegexpWithMessage("double/a/a.go", `_ "mod.com/double/b"`, "import cycle not allowed"),
1605			env.DiagnosticAtRegexpWithMessage("triple/a/a.go", `_ "mod.com/triple/b"`, "import cycle not allowed"),
1606		)
1607	})
1608}
1609
1610func TestBadImport(t *testing.T) {
1611	testenv.NeedsGo1Point(t, 14)
1612
1613	const mod = `
1614-- go.mod --
1615module mod.com
1616
1617go 1.12
1618-- main.go --
1619package main
1620
1621import (
1622	_ "nosuchpkg"
1623)
1624`
1625	t.Run("module", func(t *testing.T) {
1626		run(t, mod, func(t *testing.T, env *Env) {
1627			env.Await(
1628				env.DiagnosticAtRegexpWithMessage("main.go", `"nosuchpkg"`, `could not import nosuchpkg (no required module provides package "nosuchpkg"`),
1629			)
1630		})
1631	})
1632	t.Run("GOPATH", func(t *testing.T) {
1633		withOptions(
1634			InGOPATH(),
1635			EditorConfig{
1636				Env: map[string]string{"GO111MODULE": "off"},
1637			},
1638			Modes(Singleton),
1639		).run(t, mod, func(t *testing.T, env *Env) {
1640			env.Await(
1641				env.DiagnosticAtRegexpWithMessage("main.go", `"nosuchpkg"`, `cannot find package "nosuchpkg" in any of`),
1642			)
1643		})
1644	})
1645}
1646
1647func TestMultipleModules_Warning(t *testing.T) {
1648	const modules = `
1649-- a/go.mod --
1650module a.com
1651
1652go 1.12
1653-- a/a.go --
1654package a
1655-- b/go.mod --
1656module b.com
1657
1658go 1.12
1659-- b/b.go --
1660package b
1661`
1662	for _, go111module := range []string{"on", "auto"} {
1663		t.Run("GO111MODULE="+go111module, func(t *testing.T) {
1664			withOptions(
1665				Modes(Singleton),
1666				EditorConfig{
1667					Env: map[string]string{
1668						"GO111MODULE": go111module,
1669					},
1670				},
1671			).run(t, modules, func(t *testing.T, env *Env) {
1672				env.OpenFile("a/a.go")
1673				env.OpenFile("b/go.mod")
1674				env.Await(
1675					env.DiagnosticAtRegexp("a/a.go", "package a"),
1676					env.DiagnosticAtRegexp("b/go.mod", "module b.com"),
1677					OutstandingWork(lsp.WorkspaceLoadFailure, "gopls requires a module at the root of your workspace."),
1678				)
1679			})
1680		})
1681	}
1682
1683	// Expect no warning if GO111MODULE=auto in a directory in GOPATH.
1684	t.Run("GOPATH_GO111MODULE_auto", func(t *testing.T) {
1685		withOptions(
1686			Modes(Singleton),
1687			EditorConfig{
1688				Env: map[string]string{
1689					"GO111MODULE": "auto",
1690				},
1691			},
1692			InGOPATH(),
1693		).run(t, modules, func(t *testing.T, env *Env) {
1694			env.OpenFile("a/a.go")
1695			env.Await(
1696				OnceMet(
1697					CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
1698					NoDiagnostics("a/a.go"),
1699				),
1700				NoOutstandingWork(),
1701			)
1702		})
1703	})
1704}
1705
1706func TestNestedModules(t *testing.T) {
1707	const proxy = `
1708-- nested.com@v1.0.0/go.mod --
1709module nested.com
1710
1711go 1.12
1712-- nested.com@v1.0.0/hello/hello.go --
1713package hello
1714
1715func Hello() {}
1716`
1717
1718	const nested = `
1719-- go.mod --
1720module mod.com
1721
1722go 1.12
1723
1724require nested.com v1.0.0
1725-- go.sum --
1726nested.com v1.0.0 h1:I6spLE4CgFqMdBPc+wTV2asDO2QJ3tU0YAT+jkLeN1I=
1727nested.com v1.0.0/go.mod h1:ly53UzXQgVjSlV7wicdBB4p8BxfytuGT1Xcyv0ReJfI=
1728-- main.go --
1729package main
1730
1731import "nested.com/hello"
1732
1733func main() {
1734	hello.Hello()
1735}
1736-- nested/go.mod --
1737module nested.com
1738
1739-- nested/hello/hello.go --
1740package hello
1741
1742func Hello() {
1743	helloHelper()
1744}
1745-- nested/hello/hello_helper.go --
1746package hello
1747
1748func helloHelper() {}
1749`
1750	withOptions(
1751		ProxyFiles(proxy),
1752		Modes(Singleton),
1753	).run(t, nested, func(t *testing.T, env *Env) {
1754		// Expect a diagnostic in a nested module.
1755		env.OpenFile("nested/hello/hello.go")
1756		didOpen := CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1)
1757		env.Await(
1758			OnceMet(
1759				didOpen,
1760				env.DiagnosticAtRegexp("nested/hello/hello.go", "helloHelper"),
1761			),
1762			OnceMet(
1763				didOpen,
1764				env.DiagnosticAtRegexpWithMessage("nested/hello/hello.go", "package hello", "nested module"),
1765			),
1766			OnceMet(
1767				didOpen,
1768				OutstandingWork(lsp.WorkspaceLoadFailure, "nested module"),
1769			),
1770		)
1771	})
1772}
1773
1774func TestAdHocPackagesReloading(t *testing.T) {
1775	const nomod = `
1776-- main.go --
1777package main
1778
1779func main() {}
1780`
1781	run(t, nomod, func(t *testing.T, env *Env) {
1782		env.OpenFile("main.go")
1783		env.RegexpReplace("main.go", "{}", "{ var x int; }") // simulate typing
1784		env.Await(
1785			OnceMet(
1786				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1),
1787				NoLogMatching(protocol.Info, "packages=1"),
1788			),
1789		)
1790	})
1791}
1792