diff --git a/internal/lsp/cmd/format_test.go b/internal/lsp/cmd/format_test.go index f95c24a8..eaaf8d58 100644 --- a/internal/lsp/cmd/format_test.go +++ b/internal/lsp/cmd/format_test.go @@ -5,9 +5,7 @@ package cmd_test import ( - "bytes" "context" - "io/ioutil" "os/exec" "regexp" "strings" @@ -33,13 +31,11 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { t.Fatal(err) } args := append(mode, filename) - expect := string(r.data.Golden(tag, filename, func(golden string) error { + expect := string(r.data.Golden(tag, filename, func() ([]byte, error) { cmd := exec.Command("gofmt", args...) - buf := &bytes.Buffer{} - cmd.Stdout = buf - cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files - contents := r.normalizePaths(fixFileHeader(buf.String())) - return ioutil.WriteFile(golden, []byte(contents), 0666) + contents, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files + contents = []byte(r.normalizePaths(fixFileHeader(string(contents)))) + return contents, nil })) if expect == "" { //TODO: our error handling differs, for now just skip unformattable files diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index a1498beb..bfc8fc7f 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -9,8 +9,6 @@ import ( "context" "fmt" "go/token" - "io/ioutil" - "os" "os/exec" "sort" "strings" @@ -290,16 +288,10 @@ func (r *runner) Format(t *testing.T, data tests.Formats) { if err != nil { t.Fatal(err) } - gofmted := string(r.data.Golden("gofmt", filename, func(golden string) error { + gofmted := string(r.data.Golden("gofmt", filename, func() ([]byte, error) { cmd := exec.Command("gofmt", filename) - stdout, err := os.Create(golden) - if err != nil { - return err - } - defer stdout.Close() - cmd.Stdout = stdout - cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files - return nil + out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files + return out, nil })) edits, err := r.server.Formatting(context.Background(), &protocol.DocumentFormattingParams{ @@ -365,13 +357,13 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) { t.Errorf("for %v got %v want %v", d.Src, def, d.Def) } if hover != nil { - tag := fmt.Sprintf("hover-%d-%d", d.Def.Start().Line(), d.Def.Start().Column()) - filename, err := d.Def.URI().Filename() + tag := fmt.Sprintf("%s-hover", d.Name) + filename, err := d.Src.URI().Filename() if err != nil { t.Fatalf("failed for %v: %v", d.Def, err) } - expectHover := string(r.data.Golden(tag, filename, func(golden string) error { - return ioutil.WriteFile(golden, []byte(hover.Contents.Value), 0666) + expectHover := string(r.data.Golden(tag, filename, func() ([]byte, error) { + return []byte(hover.Contents.Value), nil })) if hover.Contents.Value != expectHover { t.Errorf("for %v got %q want %q", d.Src, hover.Contents.Value, expectHover) diff --git a/internal/lsp/reset_golden.sh b/internal/lsp/reset_golden.sh new file mode 100755 index 00000000..d52ee428 --- /dev/null +++ b/internal/lsp/reset_golden.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +find ./internal/lsp/ -name *.golden -delete +go test ./internal/lsp/ ./internal/lsp/cmd -golden diff --git a/internal/lsp/testdata/format/bad_format.gofmt-d.golden.go b/internal/lsp/testdata/format/bad_format.go.golden similarity index 51% rename from internal/lsp/testdata/format/bad_format.gofmt-d.golden.go rename to internal/lsp/testdata/format/bad_format.go.golden index 1e101d74..efbf7620 100644 --- a/internal/lsp/testdata/format/bad_format.gofmt-d.golden.go +++ b/internal/lsp/testdata/format/bad_format.go.golden @@ -1,3 +1,25 @@ +-- gofmt -- +package format //@format("package") + +import ( + "fmt" + "log" + "runtime" +) + +func hello() { + + var x int //@diag("x", "LSP", "x declared but not used") +} + +func hi() { + runtime.GOROOT() + fmt.Printf("") + + log.Printf("") +} + +-- gofmt-d -- --- format/bad_format.go.orig +++ format/bad_format.go @@ -1,16 +1,13 @@ @@ -18,3 +40,4 @@ var x int //@diag("x", "LSP", "x declared but not used") } + diff --git a/internal/lsp/testdata/format/bad_format.gofmt.golden.go b/internal/lsp/testdata/format/bad_format.gofmt.golden.go deleted file mode 100644 index 919b2d25..00000000 --- a/internal/lsp/testdata/format/bad_format.gofmt.golden.go +++ /dev/null @@ -1,19 +0,0 @@ -package format //@format("package") - -import ( - "fmt" - "log" - "runtime" -) - -func hello() { - - var x int //@diag("x", "LSP", "x declared but not used") -} - -func hi() { - runtime.GOROOT() - fmt.Printf("") - - log.Printf("") -} diff --git a/internal/lsp/testdata/format/good_format.gofmt.golden.go b/internal/lsp/testdata/format/good_format.go.golden similarity index 77% rename from internal/lsp/testdata/format/good_format.gofmt.golden.go rename to internal/lsp/testdata/format/good_format.go.golden index 01cb1610..961de474 100644 --- a/internal/lsp/testdata/format/good_format.gofmt.golden.go +++ b/internal/lsp/testdata/format/good_format.go.golden @@ -1,3 +1,4 @@ +-- gofmt -- package format //@format("package") import ( @@ -7,3 +8,6 @@ import ( func goodbye() { log.Printf("byeeeee") } + +-- gofmt-d -- + diff --git a/internal/lsp/testdata/format/good_format.gofmt-d.golden.go b/internal/lsp/testdata/format/good_format.gofmt-d.golden.go deleted file mode 100644 index e69de29b..00000000 diff --git a/internal/lsp/testdata/format/newline_format.gofmt-d.golden.go b/internal/lsp/testdata/format/newline_format.go.golden similarity index 68% rename from internal/lsp/testdata/format/newline_format.gofmt-d.golden.go rename to internal/lsp/testdata/format/newline_format.go.golden index 4470d1ef..af427dfe 100644 --- a/internal/lsp/testdata/format/newline_format.gofmt-d.golden.go +++ b/internal/lsp/testdata/format/newline_format.go.golden @@ -1,3 +1,8 @@ +-- gofmt -- +package format //@format("package") +func _() {} + +-- gofmt-d -- --- format/newline_format.go.orig +++ format/newline_format.go @@ -1,2 +1,2 @@ @@ -5,3 +10,4 @@ -func _() {} \ No newline at end of file +func _() {} + diff --git a/internal/lsp/testdata/format/newline_format.gofmt.golden.go b/internal/lsp/testdata/format/newline_format.gofmt.golden.go deleted file mode 100644 index 29459ac8..00000000 --- a/internal/lsp/testdata/format/newline_format.gofmt.golden.go +++ /dev/null @@ -1,2 +0,0 @@ -package format //@format("package") -func _() {} diff --git a/internal/lsp/testdata/format/one_line.gofmt-d.golden.go b/internal/lsp/testdata/format/one_line.go.golden similarity index 72% rename from internal/lsp/testdata/format/one_line.gofmt-d.golden.go rename to internal/lsp/testdata/format/one_line.go.golden index 44f557c9..3cdcf699 100644 --- a/internal/lsp/testdata/format/one_line.gofmt-d.golden.go +++ b/internal/lsp/testdata/format/one_line.go.golden @@ -1,6 +1,11 @@ +-- gofmt -- +package format //@format("package") + +-- gofmt-d -- --- format/one_line.go.orig +++ format/one_line.go @@ -1 +1 @@ -package format //@format("package") \ No newline at end of file +package format //@format("package") + diff --git a/internal/lsp/testdata/format/one_line.gofmt.golden.go b/internal/lsp/testdata/format/one_line.gofmt.golden.go deleted file mode 100644 index 59aca823..00000000 --- a/internal/lsp/testdata/format/one_line.gofmt.golden.go +++ /dev/null @@ -1 +0,0 @@ -package format //@format("package") diff --git a/internal/lsp/testdata/godef/a/a.go.golden b/internal/lsp/testdata/godef/a/a.go.golden new file mode 100644 index 00000000..be397359 --- /dev/null +++ b/internal/lsp/testdata/godef/a/a.go.golden @@ -0,0 +1,6 @@ +-- Random-hover -- +func Random() int +-- Random2-hover -- +func Random2(y int) int +-- err-hover -- +var err error diff --git a/internal/lsp/testdata/godef/a/a.hover-14-6.golden.go b/internal/lsp/testdata/godef/a/a.hover-14-6.golden.go deleted file mode 100644 index b2551e2c..00000000 --- a/internal/lsp/testdata/godef/a/a.hover-14-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -var err error \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/a.hover-7-6.golden.go b/internal/lsp/testdata/godef/a/a.hover-7-6.golden.go deleted file mode 100644 index 41c681ed..00000000 --- a/internal/lsp/testdata/godef/a/a.hover-7-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -type a.A string \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/a.hover-9-6.golden.go b/internal/lsp/testdata/godef/a/a.hover-9-6.golden.go deleted file mode 100644 index 16285acb..00000000 --- a/internal/lsp/testdata/godef/a/a.hover-9-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -func a.Stuff() \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/random.go.golden b/internal/lsp/testdata/godef/a/random.go.golden new file mode 100644 index 00000000..b702ee84 --- /dev/null +++ b/internal/lsp/testdata/godef/a/random.go.golden @@ -0,0 +1,6 @@ +-- PosSum-hover -- +func (*Pos).Sum() int +-- PosX-hover -- +field x int +-- RandomParamY-hover -- +var y int diff --git a/internal/lsp/testdata/godef/a/random.hover-13-2.golden.go b/internal/lsp/testdata/godef/a/random.hover-13-2.golden.go deleted file mode 100644 index 08a5f3c9..00000000 --- a/internal/lsp/testdata/godef/a/random.hover-13-2.golden.go +++ /dev/null @@ -1 +0,0 @@ -field x int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/random.hover-16-15.golden.go b/internal/lsp/testdata/godef/a/random.hover-16-15.golden.go deleted file mode 100644 index 861a9a9b..00000000 --- a/internal/lsp/testdata/godef/a/random.hover-16-15.golden.go +++ /dev/null @@ -1 +0,0 @@ -func (*Pos).Sum() int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/random.hover-3-6.golden.go b/internal/lsp/testdata/godef/a/random.hover-3-6.golden.go deleted file mode 100644 index 8daadc78..00000000 --- a/internal/lsp/testdata/godef/a/random.hover-3-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -func Random() int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/random.hover-8-14.golden.go b/internal/lsp/testdata/godef/a/random.hover-8-14.golden.go deleted file mode 100644 index a1dcda28..00000000 --- a/internal/lsp/testdata/godef/a/random.hover-8-14.golden.go +++ /dev/null @@ -1 +0,0 @@ -var y int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/a/random.hover-8-6.golden.go b/internal/lsp/testdata/godef/a/random.hover-8-6.golden.go deleted file mode 100644 index f97ad9d6..00000000 --- a/internal/lsp/testdata/godef/a/random.hover-8-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -func Random2(y int) int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/b.go.golden b/internal/lsp/testdata/godef/b/b.go.golden new file mode 100644 index 00000000..6a89f263 --- /dev/null +++ b/internal/lsp/testdata/godef/b/b.go.golden @@ -0,0 +1,16 @@ +-- A-hover -- +type a.A string +-- S1-hover -- +type S1 struct{F1 int; S2; a.A} +-- S1F1-hover -- +field F1 int +-- S1S2-hover -- +field S2 S2 +-- S2-hover -- +type S2 struct{F1 string; F2 int; *a.A} +-- S2F1-hover -- +field F1 string +-- S2F2-hover -- +field F2 int +-- Stuff-hover -- +func a.Stuff() diff --git a/internal/lsp/testdata/godef/b/b.hover-11-6.golden.go b/internal/lsp/testdata/godef/b/b.hover-11-6.golden.go deleted file mode 100644 index 63622cd0..00000000 --- a/internal/lsp/testdata/godef/b/b.hover-11-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -type S2 struct{F1 string; F2 int; *a.A} \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/b.hover-12-2.golden.go b/internal/lsp/testdata/godef/b/b.hover-12-2.golden.go deleted file mode 100644 index 718de00a..00000000 --- a/internal/lsp/testdata/godef/b/b.hover-12-2.golden.go +++ /dev/null @@ -1 +0,0 @@ -field F1 string \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/b.hover-13-2.golden.go b/internal/lsp/testdata/godef/b/b.hover-13-2.golden.go deleted file mode 100644 index e66621c4..00000000 --- a/internal/lsp/testdata/godef/b/b.hover-13-2.golden.go +++ /dev/null @@ -1 +0,0 @@ -field F2 int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/b.hover-5-6.golden.go b/internal/lsp/testdata/godef/b/b.hover-5-6.golden.go deleted file mode 100644 index 12dd895f..00000000 --- a/internal/lsp/testdata/godef/b/b.hover-5-6.golden.go +++ /dev/null @@ -1 +0,0 @@ -type S1 struct{F1 int; S2; a.A} \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/b.hover-6-2.golden.go b/internal/lsp/testdata/godef/b/b.hover-6-2.golden.go deleted file mode 100644 index 709c455d..00000000 --- a/internal/lsp/testdata/godef/b/b.hover-6-2.golden.go +++ /dev/null @@ -1 +0,0 @@ -field F1 int \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/b.hover-7-2.golden.go b/internal/lsp/testdata/godef/b/b.hover-7-2.golden.go deleted file mode 100644 index fa14bfa3..00000000 --- a/internal/lsp/testdata/godef/b/b.hover-7-2.golden.go +++ /dev/null @@ -1 +0,0 @@ -field S2 S2 \ No newline at end of file diff --git a/internal/lsp/testdata/godef/b/c.go.golden b/internal/lsp/testdata/godef/b/c.go.golden new file mode 100644 index 00000000..6d86d738 --- /dev/null +++ b/internal/lsp/testdata/godef/b/c.go.golden @@ -0,0 +1,4 @@ +-- S1-hover -- +type S1 struct{F1 int; S2; a.A} +-- S1F1-hover -- +field F1 int diff --git a/internal/lsp/testdata/godef/broken/unclosedIf.go.golden b/internal/lsp/testdata/godef/broken/unclosedIf.go.golden new file mode 100644 index 00000000..47af5862 --- /dev/null +++ b/internal/lsp/testdata/godef/broken/unclosedIf.go.golden @@ -0,0 +1,2 @@ +-- myUnclosedIf-hover -- +var myUnclosedIf string diff --git a/internal/lsp/testdata/godef/broken/unclosedIf.hover-7-7.golden.go b/internal/lsp/testdata/godef/broken/unclosedIf.hover-7-7.golden.go deleted file mode 100644 index 857d09d1..00000000 --- a/internal/lsp/testdata/godef/broken/unclosedIf.hover-7-7.golden.go +++ /dev/null @@ -1 +0,0 @@ -var myUnclosedIf string \ No newline at end of file diff --git a/internal/lsp/testdata/noparse_format/noparse_format.go.golden b/internal/lsp/testdata/noparse_format/noparse_format.go.golden new file mode 100644 index 00000000..7bdcdadc --- /dev/null +++ b/internal/lsp/testdata/noparse_format/noparse_format.go.golden @@ -0,0 +1,4 @@ +-- gofmt -- + +-- gofmt-d -- + diff --git a/internal/lsp/testdata/noparse_format/noparse_format.gofmt-d.golden.go b/internal/lsp/testdata/noparse_format/noparse_format.gofmt-d.golden.go deleted file mode 100644 index e69de29b..00000000 diff --git a/internal/lsp/testdata/noparse_format/noparse_format.gofmt.golden.go b/internal/lsp/testdata/noparse_format/noparse_format.gofmt.golden.go deleted file mode 100644 index e69de29b..00000000 diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go index 69fafcab..d810f045 100644 --- a/internal/lsp/tests/tests.go +++ b/internal/lsp/tests/tests.go @@ -12,9 +12,9 @@ import ( "go/token" "io/ioutil" "os/exec" - "path" "path/filepath" "runtime" + "sort" "strings" "testing" @@ -22,6 +22,7 @@ import ( "golang.org/x/tools/go/packages/packagestest" "golang.org/x/tools/internal/lsp/source" "golang.org/x/tools/internal/span" + "golang.org/x/tools/internal/txtar" ) // We hardcode the expected number of test cases to ensure that all tests @@ -40,10 +41,10 @@ const ( ) const ( - overlayFile = ".overlay" - goldenFile = ".golden" - inFile = ".in" - testModule = "golang.org/x/tools/internal/lsp" + overlayFileSuffix = ".overlay" + goldenFileSuffix = ".golden" + inFileSuffix = ".in" + testModule = "golang.org/x/tools/internal/lsp" ) var updateGolden = flag.Bool("golden", false, "Update golden files") @@ -78,6 +79,7 @@ type Data struct { t testing.TB fragments map[string]string dir string + golden map[string]*Golden } type Tests interface { @@ -92,6 +94,7 @@ type Tests interface { } type Definition struct { + Name string Src span.Span IsType bool Flags string @@ -110,6 +113,12 @@ type Link struct { Target string } +type Golden struct { + Filename string + Archive *txtar.Archive + Modified bool +} + func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data { t.Helper() @@ -128,19 +137,29 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data { t: t, dir: dir, fragments: map[string]string{}, + golden: map[string]*Golden{}, } files := packagestest.MustCopyFileTree(dir) overlays := map[string][]byte{} for fragment, operation := range files { - if strings.Contains(fragment, goldenFile) { + if trimmed := strings.TrimSuffix(fragment, goldenFileSuffix); trimmed != fragment { delete(files, fragment) - } else if trimmed := strings.TrimSuffix(fragment, inFile); trimmed != fragment { + goldFile := filepath.Join(dir, fragment) + archive, err := txtar.ParseFile(goldFile) + if err != nil { + t.Fatalf("could not read golden file %v: %v", fragment, err) + } + data.golden[trimmed] = &Golden{ + Filename: goldFile, + Archive: archive, + } + } else if trimmed := strings.TrimSuffix(fragment, inFileSuffix); trimmed != fragment { delete(files, fragment) files[trimmed] = operation - } else if index := strings.Index(fragment, overlayFile); index >= 0 { + } else if index := strings.Index(fragment, overlayFileSuffix); index >= 0 { delete(files, fragment) - partial := fragment[:index] + fragment[index+len(overlayFile):] + partial := fragment[:index] + fragment[index+len(overlayFileSuffix):] contents, err := ioutil.ReadFile(filepath.Join(dir, fragment)) if err != nil { t.Fatal(err) @@ -200,6 +219,12 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data { symbols[i].Children = children } } + // run a second pass to collect names for some entries. + if err := data.Exported.Expect(map[string]interface{}{ + "godef": data.collectDefinitionNames, + }); err != nil { + t.Fatal(err) + } return data } @@ -287,9 +312,23 @@ func Run(t *testing.T, tests Tests, data *Data) { } tests.Link(t, data.Links) }) + + if *updateGolden { + for _, golden := range data.golden { + if !golden.Modified { + continue + } + sort.Slice(golden.Archive.Files, func(i, j int) bool { + return golden.Archive.Files[i].Name < golden.Archive.Files[j].Name + }) + if err := ioutil.WriteFile(golden.Filename, txtar.Format(golden.Archive), 0666); err != nil { + t.Fatal(err) + } + } + } } -func (data *Data) Golden(tag string, target string, update func(golden string) error) []byte { +func (data *Data) Golden(tag string, target string, update func() ([]byte, error)) []byte { data.t.Helper() fragment, found := data.fragments[target] if !found { @@ -298,24 +337,44 @@ func (data *Data) Golden(tag string, target string, update func(golden string) e } fragment = target } - dir, file := path.Split(fragment) - prefix, suffix := file, "" - // we deliberately use the first . not the last - if dot := strings.IndexRune(file, '.'); dot >= 0 { - prefix = file[:dot] - suffix = file[dot:] + golden := data.golden[fragment] + if golden == nil { + if !*updateGolden { + data.t.Fatalf("could not find golden file %v: %v", fragment, tag) + } + golden = &Golden{ + Filename: filepath.Join(data.dir, fragment+goldenFileSuffix), + Archive: &txtar.Archive{}, + Modified: true, + } + data.golden[fragment] = golden } - golden := path.Join(data.dir, dir, prefix) + "." + tag + goldenFile + suffix - if *updateGolden { - if err := update(golden); err != nil { - data.t.Fatalf("could not update golden file %v: %v", golden, err) + var file *txtar.File + for i := range golden.Archive.Files { + f := &golden.Archive.Files[i] + if f.Name == tag { + file = f + break } } - contents, err := ioutil.ReadFile(golden) - if err != nil { - data.t.Fatalf("could not read golden file %v: %v", golden, err) + if *updateGolden { + if file == nil { + golden.Archive.Files = append(golden.Archive.Files, txtar.File{ + Name: tag, + }) + file = &golden.Archive.Files[len(golden.Archive.Files)-1] + } + contents, err := update() + if err != nil { + data.t.Fatalf("could not update golden file %v: %v", fragment, err) + } + file.Data = append(contents, '\n') // add trailing \n for txtar + golden.Modified = true } - return contents + if file == nil { + data.t.Fatalf("could not find golden contents %v: %v", fragment, tag) + } + return file.Data[:len(file.Data)-1] // drop the trailing \n } func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg string) { @@ -371,6 +430,12 @@ func (data *Data) collectTypeDefinitions(src, target span.Span) { } } +func (data *Data) collectDefinitionNames(src span.Span, name string) { + d := data.Definitions[src] + d.Name = name + data.Definitions[src] = d +} + func (data *Data) collectHighlights(name string, rng span.Span) { data.Highlights[name] = append(data.Highlights[name], rng) }