func buildTestMain(pkg *gb.Package) (*gb.Package, error) { if pkg.Scope != "test" { return nil, fmt.Errorf("package %q is not test scoped", pkg.Name) } dir := gb.Workdir(pkg) if err := mkdir(dir); err != nil { return nil, fmt.Errorf("buildTestmain: %v", err) } tests, err := loadTestFuncs(pkg.Package) if err != nil { return nil, err } if len(pkg.Package.XTestGoFiles) > 0 { // if there are external tests ensure that we import the // test package into the final binary for side effects. tests.ImportXtest = true } if err := writeTestmain(filepath.Join(dir, "_testmain.go"), tests); err != nil { return nil, err } testmain := gb.NewPackage(pkg.Context, &build.Package{ Name: pkg.Name, ImportPath: path.Join(pkg.ImportPath, "testmain"), Dir: dir, SrcRoot: pkg.SrcRoot, GoFiles: []string{"_testmain.go"}, Imports: pkg.Package.Imports, }) testmain.Scope = "test" testmain.ExtraIncludes = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") return testmain, nil }
// TestPackage returns an Action representing the steps required to build // and test this Package. func TestPackage(targets map[string]*gb.Action, pkg *gb.Package, flags []string) (*gb.Action, error) { var gofiles []string gofiles = append(gofiles, pkg.GoFiles...) gofiles = append(gofiles, pkg.TestGoFiles...) var cgofiles []string cgofiles = append(cgofiles, pkg.CgoFiles...) var imports []string imports = append(imports, pkg.Package.Imports...) imports = append(imports, pkg.Package.TestImports...) name := pkg.Name if name == "main" { // rename the main package to its package name for testing. name = filepath.Base(filepath.FromSlash(pkg.ImportPath)) } // internal tests testpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath, Dir: pkg.Dir, SrcRoot: pkg.SrcRoot, GoFiles: gofiles, CFiles: pkg.CFiles, CgoFiles: cgofiles, TestGoFiles: pkg.TestGoFiles, // passed directly to buildTestMain XTestGoFiles: pkg.XTestGoFiles, // passed directly to buildTestMain CgoCFLAGS: pkg.CgoCFLAGS, CgoCPPFLAGS: pkg.CgoCPPFLAGS, CgoCXXFLAGS: pkg.CgoCXXFLAGS, CgoLDFLAGS: pkg.CgoLDFLAGS, CgoPkgConfig: pkg.CgoPkgConfig, Imports: imports, }) testpkg.Scope = "test" testpkg.Stale = true // build dependencies deps, err := gb.BuildDependencies(targets, testpkg) if err != nil { return nil, err } // only build the internal test if there is Go source or // internal test files. var testobj *gb.Action if len(testpkg.GoFiles)+len(testpkg.CgoFiles)+len(testpkg.TestGoFiles) > 0 { var err error testobj, err = gb.Compile(testpkg, deps...) if err != nil { return nil, err } } // external tests if len(pkg.XTestGoFiles) > 0 { xtestpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, Imports: pkg.XTestImports, }) // build external test dependencies deps, err := gb.BuildDependencies(targets, xtestpkg) if err != nil { return nil, err } xtestpkg.Scope = "test" xtestpkg.Stale = true xtestpkg.ExtraIncludes = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") // if there is an internal test object, add it as a dependency. if testobj != nil { deps = append(deps, testobj) } testobj, err = gb.Compile(xtestpkg, deps...) if err != nil { return nil, err } } testmainpkg, err := buildTestMain(testpkg) if err != nil { return nil, err } testmain, err := gb.Compile(testmainpkg, testobj) if err != nil { return nil, err } cmd := exec.Command(testmainpkg.Binfile()+".test", flags...) cmd.Dir = pkg.Dir // tests run in the original source directory cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr log.Debugf("scheduling run of %v", cmd.Args) return &gb.Action{ Name: fmt.Sprintf("run: %s", cmd.Args), Deps: []*gb.Action{testmain}, Task: gb.TaskFn(func() error { return cmd.Run() }), }, nil }
// TestPackage returns an Action representing the steps required to build // and test this Package. func TestPackage(targets map[string]*gb.Action, pkg *gb.Package, flags []string) (*gb.Action, error) { var gofiles []string gofiles = append(gofiles, pkg.GoFiles...) gofiles = append(gofiles, pkg.TestGoFiles...) var cgofiles []string cgofiles = append(cgofiles, pkg.CgoFiles...) var imports []string imports = append(imports, pkg.Package.Imports...) imports = append(imports, pkg.Package.TestImports...) name := pkg.Name if name == "main" { // rename the main package to its package name for testing. name = filepath.Base(filepath.FromSlash(pkg.ImportPath)) } // internal tests testpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath, Dir: pkg.Dir, SrcRoot: pkg.SrcRoot, GoFiles: gofiles, CFiles: pkg.CFiles, CgoFiles: cgofiles, TestGoFiles: pkg.TestGoFiles, // passed directly to buildTestMain XTestGoFiles: pkg.XTestGoFiles, // passed directly to buildTestMain CgoCFLAGS: pkg.CgoCFLAGS, CgoCPPFLAGS: pkg.CgoCPPFLAGS, CgoCXXFLAGS: pkg.CgoCXXFLAGS, CgoLDFLAGS: pkg.CgoLDFLAGS, CgoPkgConfig: pkg.CgoPkgConfig, Imports: imports, }) testpkg.Scope = "test" testpkg.Stale = true // only build the internal test if there is Go source or // internal test files. var testobj *gb.Action if len(testpkg.GoFiles)+len(testpkg.CgoFiles)+len(testpkg.TestGoFiles) > 0 { // build internal testpkg dependencies deps, err := gb.BuildDependencies(targets, testpkg) if err != nil { return nil, err } testobj, err = gb.Compile(testpkg, deps...) if err != nil { return nil, err } } // external tests if len(pkg.XTestGoFiles) > 0 { xtestpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, Imports: pkg.XTestImports, }) // build external test dependencies deps, err := gb.BuildDependencies(targets, xtestpkg) if err != nil { return nil, err } xtestpkg.Scope = "test" xtestpkg.Stale = true xtestpkg.ExtraIncludes = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") // if there is an internal test object, add it as a dependency. if testobj != nil { deps = append(deps, testobj) } testobj, err = gb.Compile(xtestpkg, deps...) if err != nil { return nil, err } } testmainpkg, err := buildTestMain(testpkg) if err != nil { return nil, err } testmain, err := gb.Compile(testmainpkg, testobj) if err != nil { return nil, err } cmd := exec.Command(testmainpkg.Binfile(), flags...) cmd.Dir = pkg.Dir // tests run in the original source directory var output bytes.Buffer cmd.Stdout = &output cmd.Stderr = &output logInfoFn := func(fn func() error, format string, args ...interface{}) func() error { return func() error { err := fn() if err != nil { fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) io.Copy(os.Stdout, &output) } else { fmt.Println(pkg.ImportPath) } return err } } // test binaries can be very large, so always unlink the // binary after the test has run to free up temporary space // technically this is done by ctx.Destroy(), but freeing // the space earlier is important for projects with many // packages withcleanup := func(fn func() error) func() error { return func() error { file := testmainpkg.Binfile() err := fn() os.Remove(file) return err } } fn := withcleanup(cmd.Run) fn = logInfoFn(fn, pkg.ImportPath) // When used with the concurrent executor, building deps and // linking the test binary can cause a lot of disk space to be // pinned as linking will tend to occur more frequenty than retiring // tests. // // To solve this, we merge the testmain compile step (which includes // linking) and the execute and cleanup steps so they are executed // as one atomic operation. return &gb.Action{ Name: fmt.Sprintf("run: %s", cmd.Args), Deps: testmain.Deps, Run: func() error { err := testmain.Run() if err != nil { return err } return fn() }, }, nil }
func testPackage(targets map[string]gb.PkgTarget, pkg *gb.Package, flags []string) gb.Target { var gofiles []string gofiles = append(gofiles, pkg.GoFiles...) gofiles = append(gofiles, pkg.TestGoFiles...) var cgofiles []string cgofiles = append(cgofiles, pkg.CgoFiles...) var imports []string imports = append(imports, pkg.Package.Imports...) imports = append(imports, pkg.Package.TestImports...) name := pkg.Name if name == "main" { // rename the main package to its package name for testing. name = filepath.Base(filepath.FromSlash(pkg.ImportPath)) } // internal tests testpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath, Dir: pkg.Dir, SrcRoot: pkg.SrcRoot, GoFiles: gofiles, CFiles: pkg.CFiles, CgoFiles: cgofiles, TestGoFiles: pkg.TestGoFiles, // passed directly to buildTestMain XTestGoFiles: pkg.XTestGoFiles, // passed directly to buildTestMain CgoCFLAGS: pkg.CgoCFLAGS, CgoCPPFLAGS: pkg.CgoCPPFLAGS, CgoCXXFLAGS: pkg.CgoCXXFLAGS, CgoLDFLAGS: pkg.CgoLDFLAGS, CgoPkgConfig: pkg.CgoPkgConfig, Imports: imports, }) // build dependencies deps := gb.BuildDependencies(targets, testpkg) testpkg.Scope = "test" testpkg.Stale = true testobj := gb.Compile(testpkg, deps...) // external tests if len(pkg.XTestGoFiles) > 0 { xtestpkg := gb.NewPackage(pkg.Context, &build.Package{ Name: name, ImportPath: pkg.ImportPath + "_test", Dir: pkg.Dir, GoFiles: pkg.XTestGoFiles, Imports: pkg.XTestImports, }) // build external test dependencies deps := gb.BuildDependencies(targets, xtestpkg) xtestpkg.Scope = "test" xtestpkg.Stale = true xtestpkg.ExtraIncludes = filepath.Join(pkg.Workdir(), filepath.FromSlash(pkg.ImportPath), "_test") testobj = gb.Compile(xtestpkg, append(deps, testobj)...) } testmain, err := buildTestMain(testpkg) if err != nil { return gb.ErrTarget{err} } buildmain := gb.Ld(testmain, gb.Compile(testmain, testobj)) cmd := exec.Command(testmain.Binfile()+".test", flags...) cmd.Dir = pkg.Dir // tests run in the original source directory cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr gb.Debugf("scheduling run of %v", cmd.Args) return pkg.Run(cmd, buildmain) }