Add option to build and run tests during bootstrap

Users that want to enable this option can use the '-t' option to
bootstrap.bash when passing '-r'. Builders that want to enable this can
set the RUN_TESTS environment variable in their bootstrap.bash.

The gotestmain tools is needed to write the main functions for the test
binaries, since 'go test' doesn't work well in this environment.

Change-Id: Iec5c2b5c9c3f5e3ba0ac8677fb88f5e963f9bd3f
This commit is contained in:
Dan Willemsen 2015-06-23 17:21:00 -07:00
parent 30a80c3e5f
commit 87ba294ceb
7 changed files with 355 additions and 28 deletions

View file

@ -19,6 +19,13 @@ bootstrap_go_package(
"singleton_ctx.go",
"unpack.go",
],
testSrcs = [
"context_test.go",
"ninja_strings_test.go",
"ninja_writer_test.go",
"splice_modules_test.go",
"unpack_test.go",
],
)
bootstrap_go_package(
@ -30,6 +37,10 @@ bootstrap_go_package(
"parser/printer.go",
"parser/sort.go",
],
testSrcs = [
"parser/parser_test.go",
"parser/printer_test.go",
],
)
bootstrap_go_package(
@ -45,6 +56,9 @@ bootstrap_go_package(
"pathtools/lists.go",
"pathtools/glob.go",
],
testSrcs = [
"pathtools/glob_test.go",
],
)
bootstrap_go_package(
@ -90,3 +104,8 @@ bootstrap_go_binary(
deps = ["blueprint-parser"],
srcs = ["bpmodify/bpmodify.go"],
)
bootstrap_go_binary(
name = "gotestmain",
srcs = ["gotestmain/gotestmain.go"],
)

View file

@ -22,6 +22,8 @@
set -e
EXTRA_ARGS=""
# BOOTSTRAP should be set to the path of the bootstrap script. It can be
# either an absolute path or one relative to the build directory (which of
# these is used should probably match what's used for SRCDIR).
@ -49,16 +51,20 @@ set -e
[ -z "$GOARCH" ] && GOARCH=`go env GOHOSTARCH`
[ -z "$GOCHAR" ] && GOCHAR=`go env GOCHAR`
# If RUN_TESTS is set, behave like -t was passed in as an option.
[ ! -z "$RUN_TESTS" ] && EXTRA_ARGS="$EXTRA_ARGS -t"
usage() {
echo "Usage of ${BOOTSTRAP}:"
echo " -h: print a help message and exit"
echo " -r: regenerate ${BOOTSTRAP_MANIFEST}"
echo " -t: include tests when regenerating manifest"
}
# Parse the command line flags.
IN="$BOOTSTRAP_MANIFEST"
REGEN_BOOTSTRAP_MANIFEST=false
while getopts ":hi:r" opt; do
while getopts ":hi:rt" opt; do
case $opt in
h)
usage
@ -66,6 +72,7 @@ while getopts ":hi:r" opt; do
;;
i) IN="$OPTARG";;
r) REGEN_BOOTSTRAP_MANIFEST=true;;
t) EXTRA_ARGS="$EXTRA_ARGS -t";;
\?)
echo "Invalid option: -$OPTARG" >&2
usage
@ -83,7 +90,7 @@ if [ $REGEN_BOOTSTRAP_MANIFEST = true ]; then
# that has been built in the past.
if [ -x .bootstrap/bin/minibp ]; then
echo "Regenerating $BOOTSTRAP_MANIFEST"
./.bootstrap/bin/minibp -o $BOOTSTRAP_MANIFEST $SRCDIR/$TOPNAME
./.bootstrap/bin/minibp $EXTRA_ARGS -o $BOOTSTRAP_MANIFEST $SRCDIR/$TOPNAME
else
echo "Executable minibp not found at .bootstrap/bin/minibp" >&2
exit 1

View file

@ -31,6 +31,7 @@ var (
gcCmd = pctx.StaticVariable("gcCmd", "$goToolDir/${goChar}g")
linkCmd = pctx.StaticVariable("linkCmd", "$goToolDir/${goChar}l")
goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
// Ninja only reinvokes itself once when it regenerates a .ninja file. For
// the re-bootstrap process we need that to happen more than once, so we
@ -69,6 +70,20 @@ var (
},
"libDirFlags")
goTestMain = pctx.StaticRule("gotestmain",
blueprint.RuleParams{
Command: "$goTestMainCmd -o $out -pkg $pkg $in",
Description: "gotestmain $out",
},
"pkg")
test = pctx.StaticRule("test",
blueprint.RuleParams{
Command: "(cd $pkgSrcDir && $$OLDPWD/$in -test.short) && touch $out",
Description: "test $pkg",
},
"pkg", "pkgSrcDir")
cp = pctx.StaticRule("cp",
blueprint.RuleParams{
Command: "cp $in $out",
@ -113,6 +128,15 @@ func isGoPackageProducer(module blueprint.Module) bool {
return ok
}
type goTestProducer interface {
GoTestTarget() string
}
func isGoTestProducer(module blueprint.Module) bool {
_, ok := module.(goTestProducer)
return ok
}
func isBootstrapModule(module blueprint.Module) bool {
_, isPackage := module.(*goPackage)
_, isBinary := module.(*goBinary)
@ -140,6 +164,7 @@ type goPackage struct {
properties struct {
PkgPath string
Srcs []string
TestSrcs []string
}
// The root dir in which the package .a file is located. The full .a file
@ -149,6 +174,9 @@ type goPackage struct {
// The path of the .a file that is to be built.
archiveFile string
// The path of the test .a file that is to be built.
testArchiveFile string
// The bootstrap Config
config *Config
}
@ -172,6 +200,10 @@ func (g *goPackage) GoPackageTarget() string {
return g.archiveFile
}
func (g *goPackage) GoTestTarget() string {
return g.testArchiveFile
}
func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
name := ctx.ModuleName()
@ -181,8 +213,12 @@ func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
}
g.pkgRoot = packageRoot(ctx)
g.archiveFile = filepath.Clean(filepath.Join(g.pkgRoot,
filepath.FromSlash(g.properties.PkgPath)+".a"))
g.archiveFile = filepath.Join(g.pkgRoot,
filepath.FromSlash(g.properties.PkgPath)+".a")
if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
g.testArchiveFile = filepath.Join(testRoot(ctx),
filepath.FromSlash(g.properties.PkgPath)+".a")
}
// We only actually want to build the builder modules if we're running as
// minibp (i.e. we're generating a bootstrap Ninja file). This is to break
@ -190,9 +226,20 @@ func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
// file to be built, but building a new ninja file requires the builder to
// be built.
if g.config.generatingBootstrapper {
var deps []string
if g.config.runGoTests {
deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
g.properties.PkgPath, g.properties.Srcs,
g.properties.TestSrcs)
}
buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
g.properties.Srcs)
g.properties.Srcs, deps)
} else {
if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil)
}
phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, nil)
}
}
@ -201,9 +248,13 @@ func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
type goBinary struct {
properties struct {
Srcs []string
TestSrcs []string
PrimaryBuilder bool
}
// The path of the test .a file that is to be built.
testArchiveFile string
// The bootstrap Config
config *Config
}
@ -217,6 +268,10 @@ func newGoBinaryModuleFactory(config *Config) func() (blueprint.Module, []interf
}
}
func (g *goBinary) GoTestTarget() string {
return g.testArchiveFile
}
func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
var (
name = ctx.ModuleName()
@ -226,13 +281,24 @@ func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
binaryFile = filepath.Join(BinDir, name)
)
if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
g.testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
}
// We only actually want to build the builder modules if we're running as
// minibp (i.e. we're generating a bootstrap Ninja file). This is to break
// the circular dependence that occurs when the builder requires a new Ninja
// file to be built, but building a new ninja file requires the builder to
// be built.
if g.config.generatingBootstrapper {
buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs)
var deps []string
if g.config.runGoTests {
deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
name, g.properties.Srcs, g.properties.TestSrcs)
}
buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, deps)
var libDirFlags []string
ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
@ -261,13 +327,17 @@ func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
Inputs: []string{aoutFile},
})
} else {
if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil)
}
intermediates := []string{aoutFile, archiveFile}
phonyGoTarget(ctx, binaryFile, g.properties.Srcs, intermediates)
}
}
func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
pkgPath string, archiveFile string, srcs []string) {
pkgPath string, archiveFile string, srcs []string, orderDeps []string) {
srcDir := moduleSrcDir(ctx)
srcFiles := pathtools.PrefixPaths(srcs, srcDir)
@ -295,11 +365,83 @@ func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
Rule: gc,
Outputs: []string{archiveFile},
Inputs: srcFiles,
OrderOnly: orderDeps,
Implicits: deps,
Args: gcArgs,
})
}
func buildGoTest(ctx blueprint.ModuleContext, testRoot string,
testPkgArchive string, pkgPath string, srcs []string,
testSrcs []string) []string {
if len(testSrcs) == 0 {
return nil
}
srcDir := moduleSrcDir(ctx)
testFiles := pathtools.PrefixPaths(testSrcs, srcDir)
mainFile := filepath.Join(testRoot, "test.go")
testArchive := filepath.Join(testRoot, "test.a")
testFile := filepath.Join(testRoot, "test")
testPassed := filepath.Join(testRoot, "test.passed")
buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
append(srcs, testSrcs...), nil)
ctx.Build(pctx, blueprint.BuildParams{
Rule: goTestMain,
Outputs: []string{mainFile},
Inputs: testFiles,
Implicits: []string{"$goTestMainCmd"},
Args: map[string]string{
"pkg": pkgPath,
},
})
libDirFlags := []string{"-L " + testRoot}
ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
func(module blueprint.Module) {
dep := module.(goPackageProducer)
libDir := dep.GoPkgRoot()
libDirFlags = append(libDirFlags, "-L "+libDir)
})
ctx.Build(pctx, blueprint.BuildParams{
Rule: gc,
Outputs: []string{testArchive},
Inputs: []string{mainFile},
Implicits: []string{testPkgArchive},
Args: map[string]string{
"pkgPath": "main",
"incFlags": "-I " + testRoot,
},
})
ctx.Build(pctx, blueprint.BuildParams{
Rule: link,
Outputs: []string{testFile},
Inputs: []string{testArchive},
Implicits: []string{"$linkCmd"},
Args: map[string]string{
"libDirFlags": strings.Join(libDirFlags, " "),
},
})
ctx.Build(pctx, blueprint.BuildParams{
Rule: test,
Outputs: []string{testPassed},
Inputs: []string{testFile},
Args: map[string]string{
"pkg": pkgPath,
"pkgSrcDir": filepath.Dir(testFiles[0]),
},
})
return []string{testPassed}
}
func phonyGoTarget(ctx blueprint.ModuleContext, target string, srcs []string,
intermediates []string) {
@ -398,6 +540,10 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
primaryBuilderFile := filepath.Join(BinDir, primaryBuilderName)
if s.config.runGoTests {
primaryBuilderExtraFlags += " -t"
}
// Get the filename of the top-level Blueprints file to pass to minibp.
// This comes stored in a global variable that's set by Main.
topLevelBlueprints := filepath.Join("$srcDir",
@ -469,24 +615,40 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
// and it will trigger a reboostrap by the non-boostrap build manifest.
minibp := ctx.Rule(pctx, "minibp",
blueprint.RuleParams{
Command: fmt.Sprintf("%s -c $checkFile -m $bootstrapManifest "+
Command: fmt.Sprintf("%s $runTests -c $checkFile -m $bootstrapManifest "+
"-d $out.d -o $out $in", minibpFile),
Description: "minibp $out",
Generator: true,
Depfile: "$out.d",
},
"checkFile")
"checkFile", "runTests")
args := map[string]string{
"checkFile": "$bootstrapManifest",
}
if s.config.runGoTests {
args["runTests"] = "-t"
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: minibp,
Outputs: []string{bootstrapNinjaFile},
Inputs: []string{topLevelBlueprints},
Implicits: []string{minibpFile},
Args: map[string]string{
"checkFile": "$bootstrapManifest",
},
Args: args,
})
} else {
var allGoTests []string
ctx.VisitAllModulesIf(isGoTestProducer,
func(module blueprint.Module) {
testModule := module.(goTestProducer)
target := testModule.GoTestTarget()
if target != "" {
allGoTests = append(allGoTests, target)
}
})
// We're generating a non-bootstrapper Ninja file, so we need to set it
// up to depend on the bootstrapper Ninja file. The build.ninja target
// also has an implicit dependency on the primary builder and all other
@ -500,6 +662,7 @@ func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
// phony rule to generate it that uses the depfile.
buildNinjaDeps := []string{"$bootstrapCmd", mainNinjaFile}
buildNinjaDeps = append(buildNinjaDeps, allGoBinaries...)
buildNinjaDeps = append(buildNinjaDeps, allGoTests...)
ctx.Build(pctx, blueprint.BuildParams{
Rule: rebootstrap,
Outputs: []string{"build.ninja"},
@ -550,6 +713,13 @@ func packageRoot(ctx blueprint.ModuleContext) string {
return filepath.Join(bootstrapDir, ctx.ModuleName(), "pkg")
}
// testRoot returns the module-specific package root directory path used for
// building tests. The .a files generated here will include everything from
// packageRoot, plus the test-only code.
func testRoot(ctx blueprint.ModuleContext) string {
return filepath.Join(bootstrapDir, ctx.ModuleName(), "test")
}
// moduleSrcDir returns the path of the directory that all source file paths are
// specified relative to.
func moduleSrcDir(ctx blueprint.ModuleContext) string {

View file

@ -34,6 +34,7 @@ var (
checkFile string
manifestFile string
cpuprofile string
runGoTests bool
)
func init() {
@ -42,6 +43,7 @@ func init() {
flag.StringVar(&checkFile, "c", "", "the existing file to check against")
flag.StringVar(&manifestFile, "m", "", "the bootstrap manifest file")
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
}
func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...string) {
@ -73,6 +75,7 @@ func Main(ctx *blueprint.Context, config interface{}, extraNinjaFileDeps ...stri
bootstrapConfig := &Config{
generatingBootstrapper: generatingBootstrapper,
topLevelBlueprintsFile: flag.Arg(0),
runGoTests: runGoTests,
}
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory(bootstrapConfig))

View file

@ -45,4 +45,6 @@ type Config struct {
generatingBootstrapper bool
topLevelBlueprintsFile string
runGoTests bool
}

View file

@ -76,7 +76,7 @@ default .bootstrap/blueprint/pkg/github.com/google/blueprint.a
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.func·002
# Defined: Blueprints:56:1
# Defined: Blueprints:70:1
build $
.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
@ -100,7 +100,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.func·002
# Defined: Blueprints:35:1
# Defined: Blueprints:46:1
build .bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
: g.bootstrap.gc ${g.bootstrap.srcDir}/deptools/depfile.go | $
@ -114,7 +114,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.func·002
# Defined: Blueprints:24:1
# Defined: Blueprints:31:1
build .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a: $
g.bootstrap.gc ${g.bootstrap.srcDir}/parser/modify.go $
@ -129,7 +129,7 @@ default .bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.func·002
# Defined: Blueprints:41:1
# Defined: Blueprints:52:1
build $
.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
@ -144,7 +144,7 @@ default $
# Variant:
# Type: bootstrap_go_package
# Factory: github.com/google/blueprint/bootstrap.func·002
# Defined: Blueprints:50:1
# Defined: Blueprints:64:1
build $
.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
@ -159,7 +159,7 @@ default $
# Variant:
# Type: bootstrap_go_binary
# Factory: github.com/google/blueprint/bootstrap.func·003
# Defined: Blueprints:82:1
# Defined: Blueprints:96:1
build .bootstrap/bpfmt/obj/bpfmt.a: g.bootstrap.gc $
${g.bootstrap.srcDir}/bpfmt/bpfmt.go | ${g.bootstrap.gcCmd} $
@ -181,7 +181,7 @@ default .bootstrap/bin/bpfmt
# Variant:
# Type: bootstrap_go_binary
# Factory: github.com/google/blueprint/bootstrap.func·003
# Defined: Blueprints:88:1
# Defined: Blueprints:102:1
build .bootstrap/bpmodify/obj/bpmodify.a: g.bootstrap.gc $
${g.bootstrap.srcDir}/bpmodify/bpmodify.go | ${g.bootstrap.gcCmd} $
@ -198,12 +198,31 @@ default .bootstrap/bpmodify/obj/a.out
build .bootstrap/bin/bpmodify: g.bootstrap.cp .bootstrap/bpmodify/obj/a.out
default .bootstrap/bin/bpmodify
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: gotestmain
# Variant:
# Type: bootstrap_go_binary
# Factory: github.com/google/blueprint/bootstrap.func·003
# Defined: Blueprints:108:1
build .bootstrap/gotestmain/obj/gotestmain.a: g.bootstrap.gc $
${g.bootstrap.srcDir}/gotestmain/gotestmain.go | ${g.bootstrap.gcCmd}
pkgPath = gotestmain
default .bootstrap/gotestmain/obj/gotestmain.a
build .bootstrap/gotestmain/obj/a.out: g.bootstrap.link $
.bootstrap/gotestmain/obj/gotestmain.a | ${g.bootstrap.linkCmd}
default .bootstrap/gotestmain/obj/a.out
build .bootstrap/bin/gotestmain: g.bootstrap.cp $
.bootstrap/gotestmain/obj/a.out
default .bootstrap/bin/gotestmain
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Module: minibp
# Variant:
# Type: bootstrap_go_binary
# Factory: github.com/google/blueprint/bootstrap.func·003
# Defined: Blueprints:73:1
# Defined: Blueprints:87:1
build .bootstrap/minibp/obj/minibp.a: g.bootstrap.gc $
${g.bootstrap.srcDir}/bootstrap/minibp/main.go | ${g.bootstrap.gcCmd} $
@ -227,7 +246,7 @@ default .bootstrap/bin/minibp
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Singleton: bootstrap
# Factory: github.com/google/blueprint/bootstrap.func·007
# Factory: github.com/google/blueprint/bootstrap.func·008
rule s.bootstrap.bigbp
command = .bootstrap/bin/minibp -p -d .bootstrap/main.ninja.in.d -m ${g.bootstrap.bootstrapManifest} -o ${out} ${in}
@ -235,14 +254,15 @@ rule s.bootstrap.bigbp
description = minibp ${out}
rule s.bootstrap.minibp
command = .bootstrap/bin/minibp -c ${checkFile} -m ${g.bootstrap.bootstrapManifest} -d ${out}.d -o ${out} ${in}
command = .bootstrap/bin/minibp ${runTests} -c ${checkFile} -m ${g.bootstrap.bootstrapManifest} -d ${out}.d -o ${out} ${in}
depfile = ${out}.d
description = minibp ${out}
generator = true
build .bootstrap/main.ninja.in: s.bootstrap.bigbp $
${g.bootstrap.srcDir}/Blueprints | .bootstrap/bin/bpfmt $
.bootstrap/bin/bpmodify .bootstrap/bin/minibp
.bootstrap/bin/bpmodify .bootstrap/bin/gotestmain $
.bootstrap/bin/minibp
default .bootstrap/main.ninja.in
build .bootstrap/notAFile: phony
default .bootstrap/notAFile

106
gotestmain/gotestmain.go Normal file
View file

@ -0,0 +1,106 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gotestmain
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"os"
"strings"
"text/template"
)
var (
output = flag.String("o", "", "output filename")
pkg = flag.String("pkg", "", "test package")
exitCode = 0
)
type data struct {
Package string
Tests []string
}
func findTests(srcs []string) (tests []string) {
for _, src := range srcs {
f, err := parser.ParseFile(token.NewFileSet(), src, nil, 0)
if err != nil {
panic(err)
}
for _, obj := range f.Scope.Objects {
if obj.Kind != ast.Fun || !strings.HasPrefix(obj.Name, "Test") {
continue
}
tests = append(tests, obj.Name)
}
}
return
}
func main() {
flag.Parse()
if flag.NArg() == 0 {
fmt.Fprintln(os.Stderr, "error: must pass at least one input")
exitCode = 1
return
}
buf := &bytes.Buffer{}
d := data{
Package: *pkg,
Tests: findTests(flag.Args()),
}
err := testMainTmpl.Execute(buf, d)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(*output, buf.Bytes(), 0666)
if err != nil {
panic(err)
}
}
var testMainTmpl = template.Must(template.New("testMain").Parse(`
package main
import (
"testing"
pkg "{{.Package}}"
)
var t = []testing.InternalTest{
{{range .Tests}}
{"{{.}}", pkg.{{.}}},
{{end}}
}
func matchString(pat, str string) (bool, error) {
return true, nil
}
func main() {
testing.Main(matchString, t, nil, nil)
}
`))