wire: respect -record flag for tests (google/go-cloud#282)

Fixes google/go-cloud#281
This commit is contained in:
Issac Trotts
2018-08-06 13:35:16 -07:00
committed by Ross Light
parent 3a3760180d
commit 105b7fde1c
64 changed files with 662 additions and 150 deletions

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = "Hello, World!"
)

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = "Hello, World!"
)

View File

@@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() FooBar {
foo := provideFoo()
fooBar := provideFooBar(foo)
return fooBar
}

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectBar() (*Bar, func()) {
foo, cleanup := provideFoo()
bar, cleanup2 := provideBar(foo)
return bar, func() {
cleanup2()
cleanup()
}
}

View File

@@ -0,0 +1,27 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
fmt "fmt"
)
// Injectors from foo.go:
func injectedMessage() string {
string2 := provideMessage()
return string2
}
// foo.go:
func main() {
fmt.Println(injectedMessage())
}
func provideMessage() string {
return "Hello, World!"
}

View File

@@ -1,2 +1 @@
ERROR
not a provider not a provider

View File

@@ -0,0 +1,21 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
bar "bar"
)
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = bar.PublicMsg
)

View File

@@ -0,0 +1,21 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
os "os"
)
// Injectors from wire.go:
func injectedFile() *os.File {
file := _wireFileValue
return file
}
var (
_wireFileValue = os.Stdout
)

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
foo "foo"
)
// Injectors from wire.go:
func injectFooer() foo.Fooer {
bar := provideBar()
return bar
}

View File

@@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar(foo Foo) FooBar {
bar := provideBar()
fooBar := provideFooBar(foo, bar)
return fooBar
}

View File

@@ -1,3 +1,2 @@
ERROR
foo.Foo foo.Foo
conflicts with provider conflicts with provider

View File

@@ -0,0 +1,13 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := provideMessage()
return string2
}

View File

@@ -0,0 +1,13 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooer() Fooer {
bar := provideBar()
return bar
}

View File

@@ -0,0 +1,14 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() FooBar {
bar := provideBar()
fooBar := provideFooBar(bar, bar)
return fooBar
}

View File

@@ -1,3 +1,2 @@
ERROR
no provider found for foo.Foo no provider found for foo.Foo
no provider found for foo.Bar no provider found for foo.Bar

View File

@@ -0,0 +1,20 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
context2 "context"
)
// Injectors from wire.go:
func inject(context3 context2.Context, err2 struct{}) (context, error) {
context4, err := provide(context3)
if err != nil {
return context{}, err
}
return context4, nil
}

View File

@@ -0,0 +1,50 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
context2 "context"
fmt "fmt"
os "os"
reflect "reflect"
)
// Injectors from foo.go:
func inject(context3 context2.Context, err2 struct{}) (context, error) {
context4, err := Provide(context3)
if err != nil {
return context{}, err
}
return context4, nil
}
// foo.go:
type context struct{}
func main() {
if _, ok := reflect.TypeOf(context{}).MethodByName("Provide"); !ok {
fmt.Println("ERROR: context.Provide renamed")
os.Exit(1)
}
c, err := inject(context2.Background(), struct{}{})
if err != nil {
fmt.Println("ERROR:", err)
os.Exit(1)
}
fmt.Println(c)
}
func Provide(context2_2 context2.Context) (context, error) {
var context3 = context2.Background()
_ = context2_2
_ = context3
return context{}, nil
}
func (context) Provide() {
}

View File

@@ -0,0 +1,13 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := provideMessage()
return string2
}

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = "Hello, World!"
)

View File

@@ -1,3 +1,2 @@
ERROR
no provider found no provider found
Fooer Fooer

View File

@@ -0,0 +1,20 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
context2 "context"
)
// Injectors from wire.go:
func inject(context3 context2.Context, arg struct{}) (context, error) {
context4, err := provide(context3)
if err != nil {
return context{}, err
}
return context4, nil
}

View File

View File

@@ -0,0 +1,27 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectBaz() (Baz, func(), error) {
foo, cleanup := provideFoo()
bar, cleanup2, err := provideBar(foo)
if err != nil {
cleanup()
return 0, nil, err
}
baz, err := provideBaz(bar)
if err != nil {
cleanup2()
cleanup()
return 0, nil, err
}
return baz, func() {
cleanup2()
cleanup()
}, nil
}

View File

@@ -0,0 +1,19 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
bar "bar"
)
// Injectors from wire.go:
func injectFooBar() FooBar {
foo := provideFoo()
bar2 := bar.ProvideBar()
fooBar := provideFooBar(foo, bar2)
return fooBar
}

View File

@@ -0,0 +1,16 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFoo() (Foo, error) {
foo, err := provideFoo()
if err != nil {
return 0, err
}
return foo, nil
}

View File

@@ -0,0 +1,18 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() FooBar {
foo := provideFoo()
bar := provideBar()
fooBar := FooBar{
Foo: foo,
Bar: bar,
}
return fooBar
}

View File

@@ -0,0 +1,18 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() *FooBar {
foo := provideFoo()
bar := provideBar()
fooBar := &FooBar{
Foo: foo,
Bar: bar,
}
return fooBar
}

View File

@@ -0,0 +1,15 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() FooBar {
foo := provideFoo()
bar := provideBar()
fooBar := provideFooBar(foo, bar)
return fooBar
}

View File

@@ -1,2 +1 @@
ERROR
unexported identifier privateMsg unexported identifier privateMsg

View File

@@ -1,4 +1,3 @@
ERROR
unused provider set "unusedSet" unused provider set "unusedSet"
unused provider "provideUnused" unused provider "provideUnused"
unused value of type string unused value of type string

View File

@@ -0,0 +1,18 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectFooBar() FooBar {
foo := _wireFooValue
fooBar := provideFooBar(foo)
return fooBar
}
var (
_wireFooValue = Foo(41)
)

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() Foo {
foo := _wireFooValue
return foo
}
var (
_wireFooValue = Foo("Hello, World!")
)

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
// Injectors from wire.go:
func injectedMessage() string {
string2 := _wireStringValue
return string2
}
var (
_wireStringValue = msg
)

View File

@@ -0,0 +1,17 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
package main
import (
bar "bar"
)
// Injectors from wire.go:
func injectedMessage() string {
string2 := bar.ProvideMessage()
return string2
}

View File

@@ -31,6 +31,8 @@ import (
"time" "time"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"github.com/google/go-cloud/internal/testing/setup"
) )
func TestWire(t *testing.T) { func TestWire(t *testing.T) {
@@ -54,121 +56,117 @@ func TestWire(t *testing.T) {
test, err := loadTestCase(filepath.Join(testRoot, name), wireGo) test, err := loadTestCase(filepath.Join(testRoot, name), wireGo)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
continue
} }
tests = append(tests, test) tests = append(tests, test)
} }
wd := filepath.Join(magicGOPATH(), "src") wd := filepath.Join(magicGOPATH(), "src")
t.Run("Generate", func(t *testing.T) { if *setup.Record {
if _, err := os.Stat(filepath.Join(build.Default.GOROOT, "bin", "go")); err != nil { if _, err := os.Stat(filepath.Join(build.Default.GOROOT, "bin", "go")); err != nil {
t.Skip("go toolchain not available:", err) t.Fatal("go toolchain not available:", err)
} }
for _, test := range tests { }
test := test for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
t.Parallel() // Run Wire from a fake build context.
bctx := test.buildContext()
// Run Wire from a fake build context. gen, errs := Generate(bctx, wd, test.pkg)
bctx := test.buildContext() if len(gen) > 0 {
gen, errs := Generate(bctx, wd, test.pkg) defer t.Logf("wire_gen.go:\n%s", gen)
if len(gen) > 0 {
defer t.Logf("wire_gen.go:\n%s", gen)
}
if len(errs) > 0 {
for _, e := range errs {
t.Log(e)
}
if !test.wantError {
t.Fatal("Did not expect errors.")
}
for _, s := range test.wantErrorStrings {
if !errorListContains(errs, s) {
t.Errorf("Errors did not contain %q", s)
}
}
return
}
if len(errs) == 0 && test.wantError {
t.Fatal("wirego succeeded; want error")
}
// Find the absolute import path, since test.pkg may be a relative
// import path.
genPkg, err := bctx.Import(test.pkg, wd, build.FindOnly)
if err != nil {
t.Fatal(err)
}
// Run a `go build` with the generated output.
gopath, err := ioutil.TempDir("", "wire_test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(gopath)
if err := test.materialize(gopath); err != nil {
t.Fatal(err)
}
if len(gen) > 0 {
genPath := filepath.Join(gopath, "src", filepath.FromSlash(genPkg.ImportPath), "wire_gen.go")
if err := ioutil.WriteFile(genPath, gen, 0666); err != nil {
t.Fatal(err)
}
}
testExePath := filepath.Join(gopath, "bin", "testprog")
realBuildCtx := &build.Context{
GOARCH: bctx.GOARCH,
GOOS: bctx.GOOS,
GOROOT: bctx.GOROOT,
GOPATH: gopath,
CgoEnabled: bctx.CgoEnabled,
Compiler: bctx.Compiler,
BuildTags: bctx.BuildTags,
ReleaseTags: bctx.ReleaseTags,
}
if err := runGo(realBuildCtx, "build", "-o", testExePath, genPkg.ImportPath); err != nil {
t.Fatal("build:", err)
}
// Run the resulting program and compare its output to the expected
// output.
out, err := exec.Command(testExePath).Output()
if err != nil {
t.Error("run compiled program:", err)
}
if !bytes.Equal(out, test.wantOutput) {
t.Errorf("compiled program output = %q; want %q", out, test.wantOutput)
}
})
}
})
t.Run("Determinism", func(t *testing.T) {
const runs = 2
for _, test := range tests {
if test.wantError {
continue
} }
test := test if len(errs) > 0 {
t.Run(test.name, func(t *testing.T) { for _, e := range errs {
t.Parallel() t.Log(e)
bctx := test.buildContext()
gold, errs := Generate(bctx, wd, test.pkg)
if len(errs) > 0 {
t.Fatal("wirego:", errs)
} }
goldstr := string(gold) if !test.wantWireError {
for i := 0; i < runs-1; i++ { t.Fatal("Did not expect errors.")
out, errs := Generate(bctx, wd, test.pkg) }
if len(errs) > 0 { for _, s := range test.wantWireErrorStrings {
t.Fatal("wirego (on subsequent run):", errs) if !errorListContains(errs, s) {
} t.Errorf("Errors did not contain %q", s)
if !bytes.Equal(gold, out) {
t.Fatalf("wirego output differs when run repeatedly on same input:\n%s", diff(goldstr, string(out)))
} }
} }
}) return
}
if test.wantWireError {
t.Fatal("wire succeeded; want error")
}
if *setup.Record {
// Record ==> Build the generated Wire code,
// check that the program's output matches the
// expected output, save wire output on
// success.
if err := goBuildCheck(test, wd, bctx, gen); err != nil {
t.Fatalf("go build check failed: %v", err)
}
wireGenFile := filepath.Join(testRoot, test.name, "want", "wire_gen.go")
if err := ioutil.WriteFile(wireGenFile, gen, 0666); err != nil {
t.Fatalf("failed to write wire_gen.go file: %v", err)
}
} else {
// Replay ==> Load golden file and compare to
// generated result. This check is meant to
// detect non-deterministic behavior in the
// Generate function.
gold := test.wantWireOutput
if !bytes.Equal(gen, gold) {
t.Fatalf("wire output differs from golden file:\n%s\nIf this change is expected, run with -record to update the wire_gen.go file.", diff(string(gold), string(gen)))
}
}
})
}
}
func goBuildCheck(test *testCase, wd string, bctx *build.Context, gen []byte) error {
// Find the absolute import path, since test.pkg may be a relative
// import path.
genPkg, err := bctx.Import(test.pkg, wd, build.FindOnly)
if err != nil {
return err
}
// Run a `go build` with the generated output.
gopath, err := ioutil.TempDir("", "wire_test")
if err != nil {
return err
}
defer os.RemoveAll(gopath)
if err := test.materialize(gopath); err != nil {
return err
}
if len(gen) > 0 {
genPath := filepath.Join(gopath, "src", filepath.FromSlash(genPkg.ImportPath), "wire_gen.go")
if err := ioutil.WriteFile(genPath, gen, 0666); err != nil {
return err
} }
}) }
testExePath := filepath.Join(gopath, "bin", "testprog")
realBuildCtx := &build.Context{
GOARCH: bctx.GOARCH,
GOOS: bctx.GOOS,
GOROOT: bctx.GOROOT,
GOPATH: gopath,
CgoEnabled: bctx.CgoEnabled,
Compiler: bctx.Compiler,
BuildTags: bctx.BuildTags,
ReleaseTags: bctx.ReleaseTags,
}
if err := runGo(realBuildCtx, "build", "-o", testExePath, genPkg.ImportPath); err != nil {
return fmt.Errorf("build: %v", err)
}
// Run the resulting program and compare its output to the expected
// output.
out, err := exec.Command(testExePath).Output()
if err != nil {
return fmt.Errorf("run compiled program: %v", err)
}
if !bytes.Equal(out, test.wantProgramOutput) {
return fmt.Errorf("compiled program output = %q; want %q", out, test.wantProgramOutput)
}
return nil
} }
func TestUnexport(t *testing.T) { func TestUnexport(t *testing.T) {
@@ -284,13 +282,13 @@ func isIdent(s string) bool {
} }
type testCase struct { type testCase struct {
name string name string
pkg string pkg string
goFiles map[string][]byte goFiles map[string][]byte
wantOutput []byte wantProgramOutput []byte
wantWireOutput []byte
wantError bool wantWireError bool
wantErrorStrings []string wantWireErrorStrings []string
} }
// loadTestCase reads a test case from a directory. // loadTestCase reads a test case from a directory.
@@ -298,26 +296,52 @@ type testCase struct {
// The directory structure is: // The directory structure is:
// //
// root/ // root/
// pkg file containing the package name containing the inject function //
// (must also be package main) // pkg
// out.txt file containing the expected output, or starting with the // file containing the package name containing the inject function
// magic line "ERROR" if this test should cause generation to // (must also be package main)
// fail (any subsequent lines are substrings that should //
// appear in the errors). // ...
// ... any Go files found recursively placed under GOPATH/src/... // any Go files found recursively placed under GOPATH/src/...
//
// want/
//
// wire_errs.txt
// expected errors from the Wire Generate function,
// missing if no errors expected
//
// wire_gen.go
// verified output of wire from a test run with
// -record, missing if wire_errs.txt is present
//
// program_out.txt
// expected output from the final compiled program,
// missing if wire_errs.txt is present
//
func loadTestCase(root string, wireGoSrc []byte) (*testCase, error) { func loadTestCase(root string, wireGoSrc []byte) (*testCase, error) {
name := filepath.Base(root) name := filepath.Base(root)
pkg, err := ioutil.ReadFile(filepath.Join(root, "pkg")) pkg, err := ioutil.ReadFile(filepath.Join(root, "pkg"))
if err != nil { if err != nil {
return nil, fmt.Errorf("load test case %s: %v", name, err) return nil, fmt.Errorf("load test case %s: %v", name, err)
} }
out, err := ioutil.ReadFile(filepath.Join(root, "out.txt")) var wantProgramOutput []byte
if err != nil { var wantWireOutput []byte
return nil, fmt.Errorf("load test case %s: %v", name, err) wireErrb, err := ioutil.ReadFile(filepath.Join(root, "want", "wire_errs.txt"))
} wantWireError := err == nil
wantErrorStrings, wantError := parseGoldenOutput(out) var wantWireErrorStrings []string
if wantError { if wantWireError {
out = nil wantWireErrorStrings = strings.Split(strings.TrimSpace(string(wireErrb)), "\n")
} else {
if !*setup.Record {
wantWireOutput, err = ioutil.ReadFile(filepath.Join(root, "want", "wire_gen.go"))
if err != nil {
return nil, fmt.Errorf("load test case %s: %v. If this is a new testcase, run with -record to generate the wire_gen.go file.", name, err)
}
}
wantProgramOutput, err = ioutil.ReadFile(filepath.Join(root, "want", "program_out.txt"))
if err != nil {
return nil, fmt.Errorf("load test case %s: %v", name, err)
}
} }
goFiles := map[string][]byte{ goFiles := map[string][]byte{
"github.com/google/go-cloud/wire/wire.go": wireGoSrc, "github.com/google/go-cloud/wire/wire.go": wireGoSrc,
@@ -344,12 +368,13 @@ func loadTestCase(root string, wireGoSrc []byte) (*testCase, error) {
return nil, fmt.Errorf("load test case %s: %v", name, err) return nil, fmt.Errorf("load test case %s: %v", name, err)
} }
return &testCase{ return &testCase{
name: name, name: name,
pkg: string(bytes.TrimSpace(pkg)), pkg: string(bytes.TrimSpace(pkg)),
goFiles: goFiles, goFiles: goFiles,
wantOutput: out, wantWireOutput: wantWireOutput,
wantError: wantError, wantProgramOutput: wantProgramOutput,
wantErrorStrings: wantErrorStrings, wantWireError: wantWireError,
wantWireErrorStrings: wantWireErrorStrings,
}, nil }, nil
} }
@@ -600,19 +625,6 @@ func runDiff(a, b []byte) ([]byte, error) {
return out, err return out, err
} }
func parseGoldenOutput(out []byte) (errorStrings []string, wantError bool) {
const errorPrefix = "ERROR\n"
if !bytes.HasPrefix(out, []byte(errorPrefix)) {
return nil, false
}
// Skip past first line.
out = out[len(errorPrefix):]
// Remove any leading or trailing blank lines.
out = bytes.Trim(out, "\n")
// Split lines.
return strings.Split(string(out), "\n"), true
}
func errorListContains(errs []error, substr string) bool { func errorListContains(errs []error, substr string) bool {
for _, e := range errs { for _, e := range errs {
if strings.Contains(e.Error(), substr) { if strings.Contains(e.Error(), substr) {