go/packages/packagestest: allow tests to also specify overlay contents

This is needed to write the more advanced test cases for editor integrated
tools.
The expectation system also uses the overlay rather than the file if it is
supplied.

Change-Id: I8fd21f4efe5ac5869fa6e25d3cd0d5096051e5e5
Reviewed-on: https://go-review.googlesource.com/c/153240
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2018-12-07 13:58:01 -05:00
parent 8634b1ecd3
commit 4c874b978a
3 changed files with 50 additions and 28 deletions

View File

@ -146,7 +146,11 @@ func (e *Exported) getNotes() error {
} }
for _, pkg := range pkgs { for _, pkg := range pkgs {
for _, filename := range pkg.GoFiles { for _, filename := range pkg.GoFiles {
l, err := expect.Parse(e.fset, filename, nil) content, err := e.FileContents(filename)
if err != nil {
return err
}
l, err := expect.Parse(e.fset, filename, content)
if err != nil { if err != nil {
return fmt.Errorf("Failed to extract expectations: %v", err) return fmt.Errorf("Failed to extract expectations: %v", err)
} }
@ -337,7 +341,7 @@ func (e *Exported) rangeConverter(n *expect.Note, args []interface{}) (Range, []
return mark, args, nil return mark, args, nil
} }
case string: case string:
start, end, err := expect.MatchBefore(e.fset, e.fileContents, n.Pos, arg) start, end, err := expect.MatchBefore(e.fset, e.FileContents, n.Pos, arg)
if err != nil { if err != nil {
return Range{}, nil, err return Range{}, nil, err
} }
@ -346,7 +350,7 @@ func (e *Exported) rangeConverter(n *expect.Note, args []interface{}) (Range, []
} }
return Range{Start: start, End: end}, args, nil return Range{Start: start, End: end}, args, nil
case *regexp.Regexp: case *regexp.Regexp:
start, end, err := expect.MatchBefore(e.fset, e.fileContents, n.Pos, arg) start, end, err := expect.MatchBefore(e.fset, e.FileContents, n.Pos, arg)
if err != nil { if err != nil {
return Range{}, nil, err return Range{}, nil, err
} }

View File

@ -39,6 +39,11 @@ type Module struct {
// be a string or byte slice, in which case it is the contents of the // be a string or byte slice, in which case it is the contents of the
// file, otherwise it must be a Writer function. // file, otherwise it must be a Writer function.
Files map[string]interface{} Files map[string]interface{}
// Overlay is the set of source file overlays for the module.
// The keys are the file fragment as in the Files configuration.
// The values are the in memory overlay content for the file.
Overlay map[string][]byte
} }
// A Writer is a function that writes out a test file. // A Writer is a function that writes out a test file.
@ -53,13 +58,15 @@ type Exported struct {
// Exactly what it will contain varies depending on the Exporter being used. // Exactly what it will contain varies depending on the Exporter being used.
Config *packages.Config Config *packages.Config
temp string // the temporary directory that was exported to // Modules is the module description that was used to produce this exported data set.
primary string // the first non GOROOT module that was exported Modules []Module
written map[string]map[string]string // the full set of exported files
fset *token.FileSet // The file set used when parsing expectations temp string // the temporary directory that was exported to
notes []*expect.Note // The list of expectations extracted from go source files primary string // the first non GOROOT module that was exported
markers map[string]Range // The set of markers extracted from go source files written map[string]map[string]string // the full set of exported files
contents map[string][]byte fset *token.FileSet // The file set used when parsing expectations
notes []*expect.Note // The list of expectations extracted from go source files
markers map[string]Range // The set of markers extracted from go source files
} }
// Exporter implementations are responsible for converting from the generic description of some // Exporter implementations are responsible for converting from the generic description of some
@ -126,14 +133,15 @@ func Export(t testing.TB, exporter Exporter, modules []Module) *Exported {
} }
exported := &Exported{ exported := &Exported{
Config: &packages.Config{ Config: &packages.Config{
Dir: temp, Dir: temp,
Env: append(os.Environ(), "GOPACKAGESDRIVER=off"), Env: append(os.Environ(), "GOPACKAGESDRIVER=off"),
Overlay: make(map[string][]byte),
}, },
temp: temp, Modules: modules,
primary: modules[0].Name, temp: temp,
written: map[string]map[string]string{}, primary: modules[0].Name,
fset: token.NewFileSet(), written: map[string]map[string]string{},
contents: map[string][]byte{}, fset: token.NewFileSet(),
} }
defer func() { defer func() {
if t.Failed() || t.Skipped() { if t.Failed() || t.Skipped() {
@ -165,6 +173,10 @@ func Export(t testing.TB, exporter Exporter, modules []Module) *Exported {
t.Fatalf("Invalid type %T in files, must be string or Writer", value) t.Fatalf("Invalid type %T in files, must be string or Writer", value)
} }
} }
for fragment, value := range module.Overlay {
fullpath := exporter.Filename(exported, module.Name, filepath.FromSlash(fragment))
exported.Config.Overlay[fullpath] = value
}
} }
if err := exporter.Finalize(exported); err != nil { if err := exporter.Finalize(exported); err != nil {
t.Fatal(err) t.Fatal(err)
@ -282,8 +294,11 @@ func (e *Exported) File(module, fragment string) string {
return "" return ""
} }
func (e *Exported) fileContents(filename string) ([]byte, error) { // FileContents returns the contents of the specified file.
if content, found := e.contents[filename]; found { // It will use the overlay if the file is present, otherwise it will read it
// from disk.
func (e *Exported) FileContents(filename string) ([]byte, error) {
if content, found := e.Config.Overlay[filename]; found {
return content, nil return content, nil
} }
content, err := ioutil.ReadFile(filename) content, err := ioutil.ReadFile(filename)

View File

@ -5,7 +5,6 @@
package packagestest_test package packagestest_test
import ( import (
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -17,7 +16,11 @@ var testdata = []packagestest.Module{{
Name: "golang.org/fake1", Name: "golang.org/fake1",
Files: map[string]interface{}{ Files: map[string]interface{}{
"a.go": packagestest.Symlink("testdata/a.go"), "a.go": packagestest.Symlink("testdata/a.go"),
"b.go": "package fake1", "b.go": "invalid file contents",
},
Overlay: map[string][]byte{
"b.go": []byte("package fake1"),
"c.go": []byte("package fake1"),
}, },
}, { }, {
Name: "golang.org/fake2", Name: "golang.org/fake2",
@ -33,7 +36,7 @@ var testdata = []packagestest.Module{{
type fileTest struct { type fileTest struct {
module, fragment, expect string module, fragment, expect string
check func(t *testing.T, filename string) check func(t *testing.T, exported *packagestest.Exported, filename string)
} }
func checkFiles(t *testing.T, exported *packagestest.Exported, tests []fileTest) { func checkFiles(t *testing.T, exported *packagestest.Exported, tests []fileTest) {
@ -46,14 +49,14 @@ func checkFiles(t *testing.T, exported *packagestest.Exported, tests []fileTest)
t.Errorf("Got file %v, expected %v", got, expect) t.Errorf("Got file %v, expected %v", got, expect)
} }
if test.check != nil { if test.check != nil {
test.check(t, got) test.check(t, exported, got)
} }
} }
} }
func checkLink(expect string) func(t *testing.T, filename string) { func checkLink(expect string) func(t *testing.T, exported *packagestest.Exported, filename string) {
expect = filepath.FromSlash(expect) expect = filepath.FromSlash(expect)
return func(t *testing.T, filename string) { return func(t *testing.T, exported *packagestest.Exported, filename string) {
if target, err := os.Readlink(filename); err != nil { if target, err := os.Readlink(filename); err != nil {
t.Errorf("Error checking link %v: %v", filename, err) t.Errorf("Error checking link %v: %v", filename, err)
} else if target != expect { } else if target != expect {
@ -62,9 +65,9 @@ func checkLink(expect string) func(t *testing.T, filename string) {
} }
} }
func checkContent(expect string) func(t *testing.T, filename string) { func checkContent(expect string) func(t *testing.T, exported *packagestest.Exported, filename string) {
return func(t *testing.T, filename string) { return func(t *testing.T, exported *packagestest.Exported, filename string) {
if content, err := ioutil.ReadFile(filename); err != nil { if content, err := exported.FileContents(filename); err != nil {
t.Errorf("Error reading %v: %v", filename, err) t.Errorf("Error reading %v: %v", filename, err)
} else if string(content) != expect { } else if string(content) != expect {
t.Errorf("Content of %v does not match, got %v expected %v", filename, string(content), expect) t.Errorf("Content of %v does not match, got %v expected %v", filename, string(content), expect)