in sandbox.go [217:307]
func getTestProg(src []byte) []byte {
fset := token.NewFileSet()
// Early bail for most cases.
f, err := parser.ParseFile(fset, progName, src, parser.ImportsOnly)
if err != nil || f.Name.Name != "main" {
return nil
}
// importPos stores the position to inject the "testing" import declaration, if needed.
importPos := fset.Position(f.Name.End()).Offset
var testingImported bool
for _, s := range f.Imports {
if s.Path.Value == `"testing"` && s.Name == nil {
testingImported = true
break
}
}
// Parse everything and extract test names.
f, err = parser.ParseFile(fset, progName, src, parser.ParseComments)
if err != nil {
return nil
}
var tests []string
for _, d := range f.Decls {
n, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
name := n.Name.Name
switch {
case name == "main":
// main declared as a method will not obstruct creation of our main function.
if n.Recv == nil {
return nil
}
case isTest(name, "Test") && isTestFunc(n):
tests = append(tests, name)
}
}
// Tests imply imported "testing" package in the code.
// If there is no import, bail to let the compiler produce an error.
if !testingImported && len(tests) > 0 {
return nil
}
// We emulate "go test". An example with no "Output" comment is compiled,
// but not executed. An example with no text after "Output:" is compiled,
// executed, and expected to produce no output.
var ex []*doc.Example
// exNoOutput indicates whether an example with no output is found.
// We need to compile the program containing such an example even if there are no
// other tests or examples.
exNoOutput := false
for _, e := range doc.Examples(f) {
if e.Output != "" || e.EmptyOutput {
ex = append(ex, e)
}
if e.Output == "" && !e.EmptyOutput {
exNoOutput = true
}
}
if len(tests) == 0 && len(ex) == 0 && !exNoOutput {
return nil
}
if !testingImported && (len(ex) > 0 || exNoOutput) {
// In case of the program with examples and no "testing" package imported,
// add import after "package main" without modifying line numbers.
importDecl := []byte(`;import "testing";`)
src = bytes.Join([][]byte{src[:importPos], importDecl, src[importPos:]}, nil)
}
data := struct {
Tests []string
Examples []*doc.Example
}{
tests,
ex,
}
code := new(bytes.Buffer)
if err := testTmpl.Execute(code, data); err != nil {
panic(err)
}
src = append(src, code.Bytes()...)
return src
}