1
2
3
4
5 package load
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "fmt"
12 "go/ast"
13 "go/build"
14 "go/doc"
15 "go/parser"
16 "go/token"
17 "internal/lazytemplate"
18 "path/filepath"
19 "sort"
20 "strings"
21 "unicode"
22 "unicode/utf8"
23
24 "cmd/go/internal/fsys"
25 "cmd/go/internal/str"
26 "cmd/go/internal/trace"
27 )
28
29 var TestMainDeps = []string{
30
31 "os",
32 "reflect",
33 "testing",
34 "testing/internal/testdeps",
35 }
36
37 type TestCover struct {
38 Mode string
39 Local bool
40 Pkgs []*Package
41 Paths []string
42 Vars []coverInfo
43 DeclVars func(*Package, ...string) map[string]*CoverVar
44 }
45
46
47
48
49 func TestPackagesFor(ctx context.Context, opts PackageOpts, p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) {
50 pmain, ptest, pxtest = TestPackagesAndErrors(ctx, opts, p, cover)
51 for _, p1 := range []*Package{ptest, pxtest, pmain} {
52 if p1 == nil {
53
54 continue
55 }
56 if p1.Error != nil {
57 err = p1.Error
58 break
59 }
60 if len(p1.DepsErrors) > 0 {
61 perr := p1.DepsErrors[0]
62 err = perr
63 break
64 }
65 }
66 if pmain.Error != nil || len(pmain.DepsErrors) > 0 {
67 pmain = nil
68 }
69 if ptest.Error != nil || len(ptest.DepsErrors) > 0 {
70 ptest = nil
71 }
72 if pxtest != nil && (pxtest.Error != nil || len(pxtest.DepsErrors) > 0) {
73 pxtest = nil
74 }
75 return pmain, ptest, pxtest, err
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, cover *TestCover) (pmain, ptest, pxtest *Package) {
97 ctx, span := trace.StartSpan(ctx, "load.TestPackagesAndErrors")
98 defer span.Done()
99
100 pre := newPreload()
101 defer pre.flush()
102 allImports := append([]string{}, p.TestImports...)
103 allImports = append(allImports, p.XTestImports...)
104 pre.preloadImports(ctx, opts, allImports, p.Internal.Build)
105
106 var ptestErr, pxtestErr *PackageError
107 var imports, ximports []*Package
108 var stk ImportStack
109 var testEmbed, xtestEmbed map[string][]string
110 stk.Push(p.ImportPath + " (test)")
111 rawTestImports := str.StringList(p.TestImports)
112 for i, path := range p.TestImports {
113 p1 := loadImport(ctx, opts, pre, path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport)
114 if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath {
115
116
117
118 ptestErr = &PackageError{
119 ImportStack: importCycleStack(p1, p.ImportPath),
120 Err: errors.New("import cycle not allowed in test"),
121 IsImportCycle: true,
122 }
123 }
124 p.TestImports[i] = p1.ImportPath
125 imports = append(imports, p1)
126 }
127 var err error
128 p.TestEmbedFiles, testEmbed, err = resolveEmbed(p.Dir, p.TestEmbedPatterns)
129 if err != nil && ptestErr == nil {
130 ptestErr = &PackageError{
131 ImportStack: stk.Copy(),
132 Err: err,
133 }
134 embedErr := err.(*EmbedError)
135 ptestErr.setPos(p.Internal.Build.TestEmbedPatternPos[embedErr.Pattern])
136 }
137 stk.Pop()
138
139 stk.Push(p.ImportPath + "_test")
140 pxtestNeedsPtest := false
141 rawXTestImports := str.StringList(p.XTestImports)
142 for i, path := range p.XTestImports {
143 p1 := loadImport(ctx, opts, pre, path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport)
144 if p1.ImportPath == p.ImportPath {
145 pxtestNeedsPtest = true
146 } else {
147 ximports = append(ximports, p1)
148 }
149 p.XTestImports[i] = p1.ImportPath
150 }
151 p.XTestEmbedFiles, xtestEmbed, err = resolveEmbed(p.Dir, p.XTestEmbedPatterns)
152 if err != nil && pxtestErr == nil {
153 pxtestErr = &PackageError{
154 ImportStack: stk.Copy(),
155 Err: err,
156 }
157 embedErr := err.(*EmbedError)
158 pxtestErr.setPos(p.Internal.Build.XTestEmbedPatternPos[embedErr.Pattern])
159 }
160 stk.Pop()
161
162
163 if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local {
164 ptest = new(Package)
165 *ptest = *p
166 ptest.Error = ptestErr
167 ptest.ForTest = p.ImportPath
168 ptest.GoFiles = nil
169 ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
170 ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
171 ptest.Target = ""
172
173
174
175
176
177
178
179
180
181
182
183
184 ptest.Imports = str.StringList(p.TestImports, p.Imports)
185 ptest.Internal.Imports = append(imports, p.Internal.Imports...)
186 ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
187 ptest.Internal.ForceLibrary = true
188 ptest.Internal.BuildInfo = ""
189 ptest.Internal.Build = new(build.Package)
190 *ptest.Internal.Build = *p.Internal.Build
191 m := map[string][]token.Position{}
192 for k, v := range p.Internal.Build.ImportPos {
193 m[k] = append(m[k], v...)
194 }
195 for k, v := range p.Internal.Build.TestImportPos {
196 m[k] = append(m[k], v...)
197 }
198 ptest.Internal.Build.ImportPos = m
199 if testEmbed == nil && len(p.Internal.Embed) > 0 {
200 testEmbed = map[string][]string{}
201 }
202 for k, v := range p.Internal.Embed {
203 testEmbed[k] = v
204 }
205 ptest.Internal.Embed = testEmbed
206 ptest.EmbedFiles = str.StringList(p.EmbedFiles, p.TestEmbedFiles)
207 ptest.Internal.OrigImportPath = p.Internal.OrigImportPath
208 ptest.collectDeps()
209 } else {
210 ptest = p
211 }
212
213
214 if len(p.XTestGoFiles) > 0 {
215 pxtest = &Package{
216 PackagePublic: PackagePublic{
217 Name: p.Name + "_test",
218 ImportPath: p.ImportPath + "_test",
219 Root: p.Root,
220 Dir: p.Dir,
221 Goroot: p.Goroot,
222 GoFiles: p.XTestGoFiles,
223 Imports: p.XTestImports,
224 ForTest: p.ImportPath,
225 Module: p.Module,
226 Error: pxtestErr,
227 EmbedFiles: p.XTestEmbedFiles,
228 },
229 Internal: PackageInternal{
230 LocalPrefix: p.Internal.LocalPrefix,
231 Build: &build.Package{
232 ImportPos: p.Internal.Build.XTestImportPos,
233 },
234 Imports: ximports,
235 RawImports: rawXTestImports,
236
237 Asmflags: p.Internal.Asmflags,
238 Gcflags: p.Internal.Gcflags,
239 Ldflags: p.Internal.Ldflags,
240 Gccgoflags: p.Internal.Gccgoflags,
241 Embed: xtestEmbed,
242 OrigImportPath: p.Internal.OrigImportPath,
243 },
244 }
245 if pxtestNeedsPtest {
246 pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest)
247 }
248 pxtest.collectDeps()
249 }
250
251
252 pmain = &Package{
253 PackagePublic: PackagePublic{
254 Name: "main",
255 Dir: p.Dir,
256 GoFiles: []string{"_testmain.go"},
257 ImportPath: p.ImportPath + ".test",
258 Root: p.Root,
259 Imports: str.StringList(TestMainDeps),
260 Module: p.Module,
261 },
262 Internal: PackageInternal{
263 Build: &build.Package{Name: "main"},
264 BuildInfo: p.Internal.BuildInfo,
265 Asmflags: p.Internal.Asmflags,
266 Gcflags: p.Internal.Gcflags,
267 Ldflags: p.Internal.Ldflags,
268 Gccgoflags: p.Internal.Gccgoflags,
269 OrigImportPath: p.Internal.OrigImportPath,
270 },
271 }
272
273
274
275 stk.Push("testmain")
276 deps := TestMainDeps
277 for _, d := range LinkerDeps(p) {
278 deps = append(deps, d)
279 }
280 for _, dep := range deps {
281 if dep == ptest.ImportPath {
282 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
283 } else {
284 p1 := loadImport(ctx, opts, pre, dep, "", nil, &stk, nil, 0)
285 pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
286 }
287 }
288 stk.Pop()
289
290 if cover != nil && cover.Pkgs != nil {
291
292 seen := map[*Package]bool{p: true, ptest: true}
293 for _, p1 := range pmain.Internal.Imports {
294 seen[p1] = true
295 }
296 for _, p1 := range cover.Pkgs {
297 if seen[p1] {
298
299 continue
300 }
301 seen[p1] = true
302 pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
303 }
304 }
305
306 allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports))
307 allTestImports = append(allTestImports, pmain.Internal.Imports...)
308 allTestImports = append(allTestImports, imports...)
309 allTestImports = append(allTestImports, ximports...)
310 setToolFlags(allTestImports...)
311
312
313
314
315
316 t, err := loadTestFuncs(ptest)
317 if err != nil && pmain.Error == nil {
318 pmain.setLoadPackageDataError(err, p.ImportPath, &stk, nil)
319 }
320 t.Cover = cover
321 if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
322 pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
323 pmain.Imports = append(pmain.Imports, ptest.ImportPath)
324 t.ImportTest = true
325 }
326 if pxtest != nil {
327 pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest)
328 pmain.Imports = append(pmain.Imports, pxtest.ImportPath)
329 t.ImportXtest = true
330 }
331 pmain.collectDeps()
332
333
334
335 sort.Strings(pmain.Imports)
336 w := 0
337 for _, path := range pmain.Imports {
338 if w == 0 || path != pmain.Imports[w-1] {
339 pmain.Imports[w] = path
340 w++
341 }
342 }
343 pmain.Imports = pmain.Imports[:w]
344 pmain.Internal.RawImports = str.StringList(pmain.Imports)
345
346
347 recompileForTest(pmain, p, ptest, pxtest)
348
349
350
351
352
353 if cover != nil && cover.Local {
354 ptest.Internal.CoverMode = cover.Mode
355 var coverFiles []string
356 coverFiles = append(coverFiles, ptest.GoFiles...)
357 coverFiles = append(coverFiles, ptest.CgoFiles...)
358 ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...)
359 }
360
361 for _, cp := range pmain.Internal.Imports {
362 if len(cp.Internal.CoverVars) > 0 {
363 t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars})
364 }
365 }
366
367 data, err := formatTestmain(t)
368 if err != nil && pmain.Error == nil {
369 pmain.Error = &PackageError{Err: err}
370 }
371 if data != nil {
372 pmain.Internal.TestmainGo = &data
373 }
374
375 return pmain, ptest, pxtest
376 }
377
378
379
380 func importCycleStack(p *Package, target string) []string {
381
382 importerOf := map[string]string{p.ImportPath: ""}
383
384
385
386
387
388
389
390
391
392
393
394 q := []*Package{p}
395
396 for len(q) > 0 {
397 p := q[0]
398 q = q[1:]
399 if path := p.ImportPath; path == target {
400 var stk []string
401 for path != "" {
402 stk = append(stk, path)
403 path = importerOf[path]
404 }
405 return stk
406 }
407 for _, dep := range p.Internal.Imports {
408 if _, ok := importerOf[dep.ImportPath]; !ok {
409 importerOf[dep.ImportPath] = p.ImportPath
410 q = append(q, dep)
411 }
412 }
413 }
414
415 panic("lost path to cycle")
416 }
417
418
419
420
421
422
423
424
425
426
427 func recompileForTest(pmain, preal, ptest, pxtest *Package) {
428
429
430
431 testCopy := map[*Package]*Package{preal: ptest}
432 for _, p := range PackageList([]*Package{pmain}) {
433 if p == preal {
434 continue
435 }
436
437 didSplit := p == pmain || p == pxtest
438 split := func() {
439 if didSplit {
440 return
441 }
442 didSplit = true
443 if testCopy[p] != nil {
444 panic("recompileForTest loop")
445 }
446 p1 := new(Package)
447 testCopy[p] = p1
448 *p1 = *p
449 p1.ForTest = preal.ImportPath
450 p1.Internal.Imports = make([]*Package, len(p.Internal.Imports))
451 copy(p1.Internal.Imports, p.Internal.Imports)
452 p1.Imports = make([]string, len(p.Imports))
453 copy(p1.Imports, p.Imports)
454 p = p1
455 p.Target = ""
456 p.Internal.BuildInfo = ""
457 p.Internal.ForceLibrary = true
458 }
459
460
461 for i, imp := range p.Internal.Imports {
462 if p1 := testCopy[imp]; p1 != nil && p1 != imp {
463 split()
464 p.Internal.Imports[i] = p1
465 }
466 }
467
468
469
470
471
472
473
474 if p.Name == "main" && p != pmain && p != ptest {
475 split()
476 }
477 }
478 }
479
480
481
482 func isTestFunc(fn *ast.FuncDecl, arg string) bool {
483 if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
484 fn.Type.Params.List == nil ||
485 len(fn.Type.Params.List) != 1 ||
486 len(fn.Type.Params.List[0].Names) > 1 {
487 return false
488 }
489 ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
490 if !ok {
491 return false
492 }
493
494
495
496
497 if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
498 return true
499 }
500 if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
501 return true
502 }
503 return false
504 }
505
506
507
508
509 func isTest(name, prefix string) bool {
510 if !strings.HasPrefix(name, prefix) {
511 return false
512 }
513 if len(name) == len(prefix) {
514 return true
515 }
516 rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
517 return !unicode.IsLower(rune)
518 }
519
520 type coverInfo struct {
521 Package *Package
522 Vars map[string]*CoverVar
523 }
524
525
526
527
528 func loadTestFuncs(ptest *Package) (*testFuncs, error) {
529 t := &testFuncs{
530 Package: ptest,
531 }
532 var err error
533 for _, file := range ptest.TestGoFiles {
534 if lerr := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); lerr != nil && err == nil {
535 err = lerr
536 }
537 }
538 for _, file := range ptest.XTestGoFiles {
539 if lerr := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); lerr != nil && err == nil {
540 err = lerr
541 }
542 }
543 return t, err
544 }
545
546
547 func formatTestmain(t *testFuncs) ([]byte, error) {
548 var buf bytes.Buffer
549 if err := testmainTmpl.Execute(&buf, t); err != nil {
550 return nil, err
551 }
552 return buf.Bytes(), nil
553 }
554
555 type testFuncs struct {
556 Tests []testFunc
557 Benchmarks []testFunc
558 Examples []testFunc
559 TestMain *testFunc
560 Package *Package
561 ImportTest bool
562 NeedTest bool
563 ImportXtest bool
564 NeedXtest bool
565 Cover *TestCover
566 }
567
568
569
570 func (t *testFuncs) ImportPath() string {
571 pkg := t.Package.ImportPath
572 if strings.HasPrefix(pkg, "_/") {
573 return ""
574 }
575 if pkg == "command-line-arguments" {
576 return ""
577 }
578 return pkg
579 }
580
581
582
583
584
585 func (t *testFuncs) Covered() string {
586 if t.Cover == nil || t.Cover.Paths == nil {
587 return ""
588 }
589 return " in " + strings.Join(t.Cover.Paths, ", ")
590 }
591
592
593 func (t *testFuncs) Tested() string {
594 return t.Package.Name
595 }
596
597 type testFunc struct {
598 Package string
599 Name string
600 Output string
601 Unordered bool
602 }
603
604 var testFileSet = token.NewFileSet()
605
606 func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
607
608 src, err := fsys.Open(filename)
609 if err != nil {
610 return err
611 }
612 defer src.Close()
613 f, err := parser.ParseFile(testFileSet, filename, src, parser.ParseComments)
614 if err != nil {
615 return err
616 }
617 for _, d := range f.Decls {
618 n, ok := d.(*ast.FuncDecl)
619 if !ok {
620 continue
621 }
622 if n.Recv != nil {
623 continue
624 }
625 name := n.Name.String()
626 switch {
627 case name == "TestMain":
628 if isTestFunc(n, "T") {
629 t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
630 *doImport, *seen = true, true
631 continue
632 }
633 err := checkTestFunc(n, "M")
634 if err != nil {
635 return err
636 }
637 if t.TestMain != nil {
638 return errors.New("multiple definitions of TestMain")
639 }
640 t.TestMain = &testFunc{pkg, name, "", false}
641 *doImport, *seen = true, true
642 case isTest(name, "Test"):
643 err := checkTestFunc(n, "T")
644 if err != nil {
645 return err
646 }
647 t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
648 *doImport, *seen = true, true
649 case isTest(name, "Benchmark"):
650 err := checkTestFunc(n, "B")
651 if err != nil {
652 return err
653 }
654 t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
655 *doImport, *seen = true, true
656 }
657 }
658 ex := doc.Examples(f)
659 sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
660 for _, e := range ex {
661 *doImport = true
662 if e.Output == "" && !e.EmptyOutput {
663
664 continue
665 }
666 t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
667 *seen = true
668 }
669 return nil
670 }
671
672 func checkTestFunc(fn *ast.FuncDecl, arg string) error {
673 if !isTestFunc(fn, arg) {
674 name := fn.Name.String()
675 pos := testFileSet.Position(fn.Pos())
676 return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
677 }
678 return nil
679 }
680
681 var testmainTmpl = lazytemplate.New("main", `
682 // Code generated by 'go test'. DO NOT EDIT.
683
684 package main
685
686 import (
687 "os"
688 {{if .TestMain}}
689 "reflect"
690 {{end}}
691 "testing"
692 "testing/internal/testdeps"
693
694 {{if .ImportTest}}
695 {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
696 {{end}}
697 {{if .ImportXtest}}
698 {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
699 {{end}}
700 {{if .Cover}}
701 {{range $i, $p := .Cover.Vars}}
702 _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
703 {{end}}
704 {{end}}
705 )
706
707 var tests = []testing.InternalTest{
708 {{range .Tests}}
709 {"{{.Name}}", {{.Package}}.{{.Name}}},
710 {{end}}
711 }
712
713 var benchmarks = []testing.InternalBenchmark{
714 {{range .Benchmarks}}
715 {"{{.Name}}", {{.Package}}.{{.Name}}},
716 {{end}}
717 }
718
719 var examples = []testing.InternalExample{
720 {{range .Examples}}
721 {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
722 {{end}}
723 }
724
725 func init() {
726 testdeps.ImportPath = {{.ImportPath | printf "%q"}}
727 }
728
729 {{if .Cover}}
730
731 // Only updated by init functions, so no need for atomicity.
732 var (
733 coverCounters = make(map[string][]uint32)
734 coverBlocks = make(map[string][]testing.CoverBlock)
735 )
736
737 func init() {
738 {{range $i, $p := .Cover.Vars}}
739 {{range $file, $cover := $p.Vars}}
740 coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
741 {{end}}
742 {{end}}
743 }
744
745 func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
746 if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
747 panic("coverage: mismatched sizes")
748 }
749 if coverCounters[fileName] != nil {
750 // Already registered.
751 return
752 }
753 coverCounters[fileName] = counter
754 block := make([]testing.CoverBlock, len(counter))
755 for i := range counter {
756 block[i] = testing.CoverBlock{
757 Line0: pos[3*i+0],
758 Col0: uint16(pos[3*i+2]),
759 Line1: pos[3*i+1],
760 Col1: uint16(pos[3*i+2]>>16),
761 Stmts: numStmts[i],
762 }
763 }
764 coverBlocks[fileName] = block
765 }
766 {{end}}
767
768 func main() {
769 {{if .Cover}}
770 testing.RegisterCover(testing.Cover{
771 Mode: {{printf "%q" .Cover.Mode}},
772 Counters: coverCounters,
773 Blocks: coverBlocks,
774 CoveredPackages: {{printf "%q" .Covered}},
775 })
776 {{end}}
777 m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
778 {{with .TestMain}}
779 {{.Package}}.{{.Name}}(m)
780 os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
781 {{else}}
782 os.Exit(m.Run())
783 {{end}}
784 }
785
786 `)
787
View as plain text