internal/lsp: adding golden file support to the test harness
Also convert the format tests to use it. This means that the build bots no longer need to run gofmt. Change-Id: I5cb9d239183b17d81fdb00b38e9099d224c07e6a Reviewed-on: https://go-review.googlesource.com/c/tools/+/172973 Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
fe54fb3517
commit
2acd0f4c51
|
@ -0,0 +1,19 @@
|
||||||
|
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("")
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package format //@format("package")
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func goodbye() {
|
||||||
|
log.Printf("byeeeee")
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
package format //@format("package")
|
||||||
|
func _() {}
|
|
@ -5,13 +5,15 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
|
"flag"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -36,6 +38,15 @@ const (
|
||||||
ExpectedSignaturesCount = 19
|
ExpectedSignaturesCount = 19
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
overlayFile = ".overlay"
|
||||||
|
goldenFile = ".golden"
|
||||||
|
inFile = ".in"
|
||||||
|
testModule = "golang.org/x/tools/internal/lsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var updateGolden = flag.Bool("golden", false, "Update golden files")
|
||||||
|
|
||||||
type Diagnostics map[span.URI][]source.Diagnostic
|
type Diagnostics map[span.URI][]source.Diagnostic
|
||||||
type CompletionItems map[token.Pos]*source.CompletionItem
|
type CompletionItems map[token.Pos]*source.CompletionItem
|
||||||
type Completions map[span.Span][]token.Pos
|
type Completions map[span.Span][]token.Pos
|
||||||
|
@ -58,6 +69,10 @@ type Data struct {
|
||||||
Symbols Symbols
|
Symbols Symbols
|
||||||
symbolsChildren SymbolsChildren
|
symbolsChildren SymbolsChildren
|
||||||
Signatures Signatures
|
Signatures Signatures
|
||||||
|
|
||||||
|
t testing.TB
|
||||||
|
fragments map[string]string
|
||||||
|
dir string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tests interface {
|
type Tests interface {
|
||||||
|
@ -91,19 +106,23 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
||||||
Symbols: make(Symbols),
|
Symbols: make(Symbols),
|
||||||
symbolsChildren: make(SymbolsChildren),
|
symbolsChildren: make(SymbolsChildren),
|
||||||
Signatures: make(Signatures),
|
Signatures: make(Signatures),
|
||||||
|
|
||||||
|
t: t,
|
||||||
|
dir: dir,
|
||||||
|
fragments: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
files := packagestest.MustCopyFileTree(dir)
|
files := packagestest.MustCopyFileTree(dir)
|
||||||
overlays := map[string][]byte{}
|
overlays := map[string][]byte{}
|
||||||
for fragment, operation := range files {
|
for fragment, operation := range files {
|
||||||
if trimmed := strings.TrimSuffix(fragment, ".in"); trimmed != fragment {
|
if strings.Contains(fragment, goldenFile) {
|
||||||
|
delete(files, fragment)
|
||||||
|
} else if trimmed := strings.TrimSuffix(fragment, inFile); trimmed != fragment {
|
||||||
delete(files, fragment)
|
delete(files, fragment)
|
||||||
files[trimmed] = operation
|
files[trimmed] = operation
|
||||||
}
|
} else if index := strings.Index(fragment, overlayFile); index >= 0 {
|
||||||
const overlay = ".overlay"
|
|
||||||
if index := strings.Index(fragment, overlay); index >= 0 {
|
|
||||||
delete(files, fragment)
|
delete(files, fragment)
|
||||||
partial := fragment[:index] + fragment[index+len(overlay):]
|
partial := fragment[:index] + fragment[index+len(overlayFile):]
|
||||||
contents, err := ioutil.ReadFile(filepath.Join(dir, fragment))
|
contents, err := ioutil.ReadFile(filepath.Join(dir, fragment))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -113,12 +132,16 @@ func Load(t testing.TB, exporter packagestest.Exporter, dir string) *Data {
|
||||||
}
|
}
|
||||||
modules := []packagestest.Module{
|
modules := []packagestest.Module{
|
||||||
{
|
{
|
||||||
Name: "golang.org/x/tools/internal/lsp",
|
Name: testModule,
|
||||||
Files: files,
|
Files: files,
|
||||||
Overlay: overlays,
|
Overlay: overlays,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
data.Exported = packagestest.Export(t, exporter, modules)
|
data.Exported = packagestest.Export(t, exporter, modules)
|
||||||
|
for fragment, _ := range files {
|
||||||
|
filename := data.Exported.File(testModule, fragment)
|
||||||
|
data.fragments[filename] = fragment
|
||||||
|
}
|
||||||
|
|
||||||
// Merge the exported.Config with the view.Config.
|
// Merge the exported.Config with the view.Config.
|
||||||
data.Config = *data.Exported.Config
|
data.Config = *data.Exported.Config
|
||||||
|
@ -231,6 +254,35 @@ func Run(t *testing.T, tests Tests, data *Data) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (data *Data) Golden(tag string, target string, update func(golden string) error) []byte {
|
||||||
|
data.t.Helper()
|
||||||
|
fragment, found := data.fragments[target]
|
||||||
|
if !found {
|
||||||
|
if filepath.IsAbs(target) {
|
||||||
|
data.t.Fatalf("invalid golden file fragment %v", target)
|
||||||
|
}
|
||||||
|
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 := 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents, err := ioutil.ReadFile(golden)
|
||||||
|
if err != nil {
|
||||||
|
data.t.Fatalf("could not read golden file %v: %v", golden, err)
|
||||||
|
}
|
||||||
|
return contents
|
||||||
|
}
|
||||||
|
|
||||||
func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg string) {
|
func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg string) {
|
||||||
if _, ok := data.Diagnostics[spn.URI()]; !ok {
|
if _, ok := data.Diagnostics[spn.URI()]; !ok {
|
||||||
data.Diagnostics[spn.URI()] = []source.Diagnostic{}
|
data.Diagnostics[spn.URI()] = []source.Diagnostic{}
|
||||||
|
@ -266,11 +318,17 @@ func (data *Data) collectCompletionItems(pos token.Pos, label, detail, kind stri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *Data) collectFormats(pos token.Position) {
|
func (data *Data) collectFormats(pos token.Position) {
|
||||||
cmd := exec.Command("gofmt", pos.Filename)
|
data.Formats[pos.Filename] = string(data.Golden("gofmt", pos.Filename, func(golden string) error {
|
||||||
stdout := bytes.NewBuffer(nil)
|
cmd := exec.Command("gofmt", pos.Filename)
|
||||||
cmd.Stdout = stdout
|
stdout, err := os.Create(golden)
|
||||||
cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files
|
if err != nil {
|
||||||
data.Formats[pos.Filename] = stdout.String()
|
return err
|
||||||
|
}
|
||||||
|
defer stdout.Close()
|
||||||
|
cmd.Stdout = stdout
|
||||||
|
cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *Data) collectDefinitions(src, target span.Span) {
|
func (data *Data) collectDefinitions(src, target span.Span) {
|
||||||
|
|
Loading…
Reference in New Issue