// Simplifying wrapper around buildutil.FakeContext for single-file packages. func fakeContext(pkgs map[string]string) *build.Context { pkgs2 := make(map[string]map[string]string) for path, content := range pkgs { pkgs2[path] = map[string]string{"x.go": content} } return buildutil.FakeContext(pkgs2) }
func TestBundle(t *testing.T) { load := func(name string) string { data, err := ioutil.ReadFile(name) if err != nil { t.Fatal(err) } return string(data) } ctxt = buildutil.FakeContext(map[string]map[string]string{ "initial": { "a.go": load("testdata/src/initial/a.go"), "b.go": load("testdata/src/initial/b.go"), }, "fmt": { "print.go": `package fmt; func Println(...interface{})`, }, }) var out bytes.Buffer if err := bundle(&out, "initial", "dest", "prefix"); err != nil { t.Fatal(err) } if got, want := out.String(), load("testdata/out.golden"); got != want { t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want) if err := ioutil.WriteFile("testdata/out.got", out.Bytes(), 0644); err != nil { t.Fatal(err) } t.Log(diff("testdata/out.got", "testdata/out.golden")) } }
// Simplifying wrapper around buildutil.FakeContext for packages whose // filenames are sequentially numbered (%d.go). pkgs maps a package // import path to its list of file contents. func fakeContext(pkgs map[string][]string) *build.Context { pkgs2 := make(map[string]map[string]string) for path, files := range pkgs { filemap := make(map[string]string) for i, contents := range files { filemap[fmt.Sprintf("%d.go", i)] = contents } pkgs2[path] = filemap } return buildutil.FakeContext(pkgs2) }
// Simplifying wrapper around buildutil.FakeContext for packages whose // filenames are sequentially numbered (%d.go). pkgs maps a package // import path to its list of file contents. func fakeContext(pkgs map[string][]string) *build.Context { pkgs["fmt"] = []string{`package fmt; func Println(args ...interface{}) {}`} pkgs["io"] = []string{`package io; type Reader interface { Read(p []byte) (n int, err error); }`} pkgs2 := make(map[string]map[string]string) for path, files := range pkgs { filemap := make(map[string]string) for i, contents := range files { filemap[fmt.Sprintf("%d.go", i)] = contents } pkgs2[path] = filemap } return buildutil.FakeContext(pkgs2) }
func TestExpandPatterns(t *testing.T) { tree := make(map[string]map[string]string) for _, pkg := range []string{ "encoding", "encoding/xml", "encoding/hex", "encoding/json", "fmt", } { tree[pkg] = make(map[string]string) } ctxt := buildutil.FakeContext(tree) for _, test := range []struct { patterns string want string }{ {"", ""}, {"fmt", "fmt"}, {"nosuchpkg", "nosuchpkg"}, {"nosuchdir/...", ""}, {"...", "encoding encoding/hex encoding/json encoding/xml fmt"}, {"encoding/... -encoding/xml", "encoding encoding/hex encoding/json"}, {"... -encoding/...", "fmt"}, {"encoding", "encoding"}, {"encoding/", "encoding"}, } { var pkgs []string for pkg := range buildutil.ExpandPatterns(ctxt, strings.Fields(test.patterns)) { pkgs = append(pkgs, pkg) } sort.Strings(pkgs) got := strings.Join(pkgs, " ") if got != test.want { t.Errorf("ExpandPatterns(%s) = %s, want %s", test.patterns, got, test.want) } } }
func TestBundle(t *testing.T) { load := func(name string) string { data, err := ioutil.ReadFile(name) if err != nil { t.Fatal(err) } return string(data) } ctxt = buildutil.FakeContext(map[string]map[string]string{ "initial": { "a.go": load("testdata/src/initial/a.go"), "b.go": load("testdata/src/initial/b.go"), "c.go": load("testdata/src/initial/c.go"), }, "domain.name/importdecl": { "p.go": load("testdata/src/domain.name/importdecl/p.go"), }, "fmt": { "print.go": `package fmt; func Println(...interface{})`, }, }) os.Args = os.Args[:1] // avoid e.g. -test=short in the output out, err := bundle("initial", "github.com/dest", "dest", "prefix") if err != nil { t.Fatal(err) } if got, want := string(out), load("testdata/out.golden"); got != want { t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want) if err := ioutil.WriteFile("testdata/out.got", out, 0644); err != nil { t.Fatal(err) } t.Log(diff("testdata/out.golden", "testdata/out.got")) } }
func TestCycles(t *testing.T) { for _, test := range []struct { descr string ctxt *build.Context wantErr string }{ { "self-cycle", fakeContext(map[string]string{ "main": `package main; import _ "selfcycle"`, "selfcycle": `package selfcycle; import _ "selfcycle"`, }), `import cycle: selfcycle -> selfcycle`, }, { "three-package cycle", fakeContext(map[string]string{ "main": `package main; import _ "a"`, "a": `package a; import _ "b"`, "b": `package b; import _ "c"`, "c": `package c; import _ "a"`, }), `import cycle: c -> a -> b -> c`, }, { "self-cycle in dependency of test file", buildutil.FakeContext(map[string]map[string]string{ "main": { "main.go": `package main`, "main_test.go": `package main; import _ "a"`, }, "a": { "a.go": `package a; import _ "a"`, }, }), `import cycle: a -> a`, }, // TODO(adonovan): fix: these fail // { // "two-package cycle in dependency of test file", // buildutil.FakeContext(map[string]map[string]string{ // "main": { // "main.go": `package main`, // "main_test.go": `package main; import _ "a"`, // }, // "a": { // "a.go": `package a; import _ "main"`, // }, // }), // `import cycle: main -> a -> main`, // }, // { // "self-cycle in augmented package", // buildutil.FakeContext(map[string]map[string]string{ // "main": { // "main.go": `package main`, // "main_test.go": `package main; import _ "main"`, // }, // }), // `import cycle: main -> main`, // }, } { conf := loader.Config{ AllowErrors: true, Build: test.ctxt, } var mu sync.Mutex var allErrors []error conf.TypeChecker.Error = func(err error) { mu.Lock() allErrors = append(allErrors, err) mu.Unlock() } conf.ImportWithTests("main") prog, err := conf.Load() if err != nil { t.Errorf("%s: Load failed: %s", test.descr, err) } if prog == nil { t.Fatalf("%s: Load returned nil *Program", test.descr) } if !hasError(allErrors, test.wantErr) { t.Errorf("%s: Load() errors = %q, want %q", test.descr, allErrors, test.wantErr) } } // TODO(adonovan): // - Test that in a legal test cycle, none of the symbols // defined by augmentation are visible via import. }
func TestMoves(t *testing.T) { tests := []struct { ctxt *build.Context from, to string want map[string]string }{ // Simple example. { ctxt: fakeContext(map[string][]string{ "foo": {`package foo; type T int`}, "main": {`package main import "foo" var _ foo.T `}, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/main/0.go": `package main import "bar" var _ bar.T `, "/go/src/bar/0.go": `package bar type T int `, }, }, // Example with subpackage. { ctxt: fakeContext(map[string][]string{ "foo": {`package foo; type T int`}, "foo/sub": {`package sub; type T int`}, "main": {`package main import "foo" import "foo/sub" var _ foo.T var _ sub.T `}, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/main/0.go": `package main import "bar" import "bar/sub" var _ bar.T var _ sub.T `, "/go/src/bar/0.go": `package bar type T int `, "/go/src/bar/sub/0.go": `package sub; type T int`, }, }, // References into subpackages { ctxt: fakeContext(map[string][]string{ "foo": {`package foo; import "foo/a"; var _ a.T`}, "foo/a": {`package a; type T int`}, "foo/b": {`package b; import "foo/a"; var _ a.T`}, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/bar/0.go": `package bar import "bar/a" var _ a.T `, "/go/src/bar/a/0.go": `package a; type T int`, "/go/src/bar/b/0.go": `package b import "bar/a" var _ a.T `, }, }, // External test packages { ctxt: buildutil.FakeContext(map[string]map[string]string{ "foo": { "0.go": `package foo; type T int`, "0_test.go": `package foo_test; import "foo"; var _ foo.T`, }, "baz": { "0_test.go": `package baz_test; import "foo"; var _ foo.T`, }, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/bar/0.go": `package bar type T int `, "/go/src/bar/0_test.go": `package bar_test import "bar" var _ bar.T `, "/go/src/baz/0_test.go": `package baz_test import "bar" var _ bar.T `, }, }, } for _, test := range tests { ctxt := test.ctxt got := make(map[string]string) // Populate got with starting file set. rewriteFile and moveDirectory // will mutate got to produce resulting file set. buildutil.ForEachPackage(ctxt, func(importPath string, err error) { if err != nil { return } path := filepath.Join("/go/src", importPath, "0.go") if !buildutil.FileExists(ctxt, path) { return } f, err := ctxt.OpenFile(path) if err != nil { t.Errorf("unexpected error opening file: %s", err) return } bytes, err := ioutil.ReadAll(f) f.Close() if err != nil { t.Errorf("unexpected error reading file: %s", err) return } got[path] = string(bytes) }) rewriteFile = func(fset *token.FileSet, f *ast.File, orig string) error { var out bytes.Buffer if err := format.Node(&out, fset, f); err != nil { return err } got[orig] = out.String() return nil } moveDirectory = func(from, to string) error { for path, contents := range got { if strings.HasPrefix(path, from) { newPath := strings.Replace(path, from, to, 1) delete(got, path) got[newPath] = contents } } return nil } err := Move(ctxt, test.from, test.to, "") prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to) if err != nil { t.Errorf("%s: unexpected error: %s", prefix, err) continue } for file, wantContent := range test.want { k := filepath.FromSlash(file) gotContent, ok := got[k] delete(got, k) if !ok { // TODO(matloob): some testcases might have files that won't be // rewritten t.Errorf("%s: file %s not rewritten", prefix, file) continue } if gotContent != wantContent { t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+ "want <<<%s>>>", prefix, file, gotContent, wantContent) } } // got should now be empty for file := range got { t.Errorf("%s: unexpected rewrite of file %s", prefix, file) } } }
func TestMoves(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("broken on Windows; see golang.org/issue/16384") } tests := []struct { ctxt *build.Context from, to string want map[string]string wantWarnings []string }{ // Simple example. { ctxt: fakeContext(map[string][]string{ "foo": {`package foo; type T int`}, "main": {`package main import "foo" var _ foo.T `}, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/main/0.go": `package main import "bar" var _ bar.T `, "/go/src/bar/0.go": `package bar type T int `, }, }, // Example with subpackage. { ctxt: fakeContext(map[string][]string{ "foo": {`package foo; type T int`}, "foo/sub": {`package sub; type T int`}, "main": {`package main import "foo" import "foo/sub" var _ foo.T var _ sub.T `}, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/main/0.go": `package main import "bar" import "bar/sub" var _ bar.T var _ sub.T `, "/go/src/bar/0.go": `package bar type T int `, "/go/src/bar/sub/0.go": `package sub; type T int`, }, }, // References into subpackages { ctxt: fakeContext(map[string][]string{ "foo": {`package foo; import "foo/a"; var _ a.T`}, "foo/a": {`package a; type T int`}, "foo/b": {`package b; import "foo/a"; var _ a.T`}, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/bar/0.go": `package bar import "bar/a" var _ a.T `, "/go/src/bar/a/0.go": `package a; type T int`, "/go/src/bar/b/0.go": `package b import "bar/a" var _ a.T `, }, }, // External test packages { ctxt: buildutil.FakeContext(map[string]map[string]string{ "foo": { "0.go": `package foo; type T int`, "0_test.go": `package foo_test; import "foo"; var _ foo.T`, }, "baz": { "0_test.go": `package baz_test; import "foo"; var _ foo.T`, }, }), from: "foo", to: "bar", want: map[string]string{ "/go/src/bar/0.go": `package bar type T int `, "/go/src/bar/0_test.go": `package bar_test import "bar" var _ bar.T `, "/go/src/baz/0_test.go": `package baz_test import "bar" var _ bar.T `, }, }, // package import comments { ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}), from: "foo", to: "bar", want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar" `}, }, { ctxt: fakeContext(map[string][]string{"foo": {`package foo /* import "baz" */`}}), from: "foo", to: "bar", want: map[string]string{"/go/src/bar/0.go": `package bar /* import "bar" */ `}, }, { ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}), from: "foo", to: "bar", want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar" `}, }, { ctxt: fakeContext(map[string][]string{"foo": {`package foo // import " this is not an import comment`}}), from: "foo", to: "bar", want: map[string]string{"/go/src/bar/0.go": `package bar // import " this is not an import comment `}, }, { ctxt: fakeContext(map[string][]string{"foo": {`package foo /* import " this is not an import comment */`}}), from: "foo", to: "bar", want: map[string]string{"/go/src/bar/0.go": `package bar /* import " this is not an import comment */ `}, }, // Import name conflict generates a warning, not an error. { ctxt: fakeContext(map[string][]string{ "x": {}, "a": {`package a; type A int`}, "b": {`package b; type B int`}, "conflict": {`package conflict import "a" import "b" var _ a.A var _ b.B `}, "ok": {`package ok import "b" var _ b.B `}, }), from: "b", to: "x/a", want: map[string]string{ "/go/src/a/0.go": `package a; type A int`, "/go/src/ok/0.go": `package ok import "x/a" var _ a.B `, "/go/src/conflict/0.go": `package conflict import "a" import "x/a" var _ a.A var _ b.B `, "/go/src/x/a/0.go": `package a type B int `, }, wantWarnings: []string{ `/go/src/conflict/0.go:4:8: renaming this imported package name "b" to "a"`, `/go/src/conflict/0.go:3:8: conflicts with imported package name in same block`, `/go/src/conflict/0.go:3:8: skipping update of this file`, }, }, // Rename with same base name. { ctxt: fakeContext(map[string][]string{ "x": {}, "y": {}, "x/foo": {`package foo type T int `}, "main": {`package main; import "x/foo"; var _ foo.T`}, }), from: "x/foo", to: "y/foo", want: map[string]string{ "/go/src/y/foo/0.go": `package foo type T int `, "/go/src/main/0.go": `package main import "y/foo" var _ foo.T `, }, }, } for _, test := range tests { ctxt := test.ctxt got := make(map[string]string) // Populate got with starting file set. rewriteFile and moveDirectory // will mutate got to produce resulting file set. buildutil.ForEachPackage(ctxt, func(importPath string, err error) { if err != nil { return } path := filepath.Join("/go/src", importPath, "0.go") if !buildutil.FileExists(ctxt, path) { return } f, err := ctxt.OpenFile(path) if err != nil { t.Errorf("unexpected error opening file: %s", err) return } bytes, err := ioutil.ReadAll(f) f.Close() if err != nil { t.Errorf("unexpected error reading file: %s", err) return } got[path] = string(bytes) }) var warnings []string reportError = func(posn token.Position, message string) { warning := fmt.Sprintf("%s:%d:%d: %s", filepath.ToSlash(posn.Filename), // for MS Windows posn.Line, posn.Column, message) warnings = append(warnings, warning) } writeFile = func(filename string, content []byte) error { got[filename] = string(content) return nil } moveDirectory = func(from, to string) error { for path, contents := range got { if strings.HasPrefix(path, from) { newPath := strings.Replace(path, from, to, 1) delete(got, path) got[newPath] = contents } } return nil } err := Move(ctxt, test.from, test.to, "") prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to) if err != nil { t.Errorf("%s: unexpected error: %s", prefix, err) continue } if !reflect.DeepEqual(warnings, test.wantWarnings) { t.Errorf("%s: unexpected warnings:\n%s\nwant:\n%s", prefix, strings.Join(warnings, "\n"), strings.Join(test.wantWarnings, "\n")) } for file, wantContent := range test.want { k := filepath.FromSlash(file) gotContent, ok := got[k] delete(got, k) if !ok { // TODO(matloob): some testcases might have files that won't be // rewritten t.Errorf("%s: file %s not rewritten", prefix, file) continue } if gotContent != wantContent { t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+ "want <<<%s>>>", prefix, file, gotContent, wantContent) } } // got should now be empty for file := range got { t.Errorf("%s: unexpected rewrite of file %s", prefix, file) } } }