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
372
373 pmain.Internal.TestmainGo = &data
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 FuzzTargets []testFunc
559 Examples []testFunc
560 TestMain *testFunc
561 Package *Package
562 ImportTest bool
563 NeedTest bool
564 ImportXtest bool
565 NeedXtest bool
566 Cover *TestCover
567 }
568
569
570
571 func (t *testFuncs) ImportPath() string {
572 pkg := t.Package.ImportPath
573 if strings.HasPrefix(pkg, "_/") {
574 return ""
575 }
576 if pkg == "command-line-arguments" {
577 return ""
578 }
579 return pkg
580 }
581
582
583
584
585
586 func (t *testFuncs) Covered() string {
587 if t.Cover == nil || t.Cover.Paths == nil {
588 return ""
589 }
590 return " in " + strings.Join(t.Cover.Paths, ", ")
591 }
592
593
594 func (t *testFuncs) Tested() string {
595 return t.Package.Name
596 }
597
598 type testFunc struct {
599 Package string
600 Name string
601 Output string
602 Unordered bool
603 }
604
605 var testFileSet = token.NewFileSet()
606
607 func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
608
609 src, err := fsys.Open(filename)
610 if err != nil {
611 return err
612 }
613 defer src.Close()
614 f, err := parser.ParseFile(testFileSet, filename, src, parser.ParseComments)
615 if err != nil {
616 return err
617 }
618 for _, d := range f.Decls {
619 n, ok := d.(*ast.FuncDecl)
620 if !ok {
621 continue
622 }
623 if n.Recv != nil {
624 continue
625 }
626 name := n.Name.String()
627 switch {
628 case name == "TestMain":
629 if isTestFunc(n, "T") {
630 t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
631 *doImport, *seen = true, true
632 continue
633 }
634 err := checkTestFunc(n, "M")
635 if err != nil {
636 return err
637 }
638 if t.TestMain != nil {
639 return errors.New("multiple definitions of TestMain")
640 }
641 t.TestMain = &testFunc{pkg, name, "", false}
642 *doImport, *seen = true, true
643 case isTest(name, "Test"):
644 err := checkTestFunc(n, "T")
645 if err != nil {
646 return err
647 }
648 t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
649 *doImport, *seen = true, true
650 case isTest(name, "Benchmark"):
651 err := checkTestFunc(n, "B")
652 if err != nil {
653 return err
654 }
655 t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
656 *doImport, *seen = true, true
657 case isTest(name, "Fuzz"):
658 err := checkTestFunc(n, "F")
659 if err != nil {
660 return err
661 }
662 t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false})
663 *doImport, *seen = true, true
664 }
665 }
666 ex := doc.Examples(f)
667 sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
668 for _, e := range ex {
669 *doImport = true
670 if e.Output == "" && !e.EmptyOutput {
671
672 continue
673 }
674 t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
675 *seen = true
676 }
677 return nil
678 }
679
680 func checkTestFunc(fn *ast.FuncDecl, arg string) error {
681 var why string
682 if !isTestFunc(fn, arg) {
683 why = fmt.Sprintf("must be: func %s(%s *testing.%s)", fn.Name.String(), strings.ToLower(arg), arg)
684 }
685 if fn.Type.TypeParams.NumFields() > 0 {
686 why = "test functions cannot have type parameters"
687 }
688 if why != "" {
689 pos := testFileSet.Position(fn.Pos())
690 return fmt.Errorf("%s: wrong signature for %s, %s", pos, fn.Name.String(), why)
691 }
692 return nil
693 }
694
695 var testmainTmpl = lazytemplate.New("main", `
696 // Code generated by 'go test'. DO NOT EDIT.
697
698 package main
699
700 import (
701 "os"
702 {{if .TestMain}}
703 "reflect"
704 {{end}}
705 "testing"
706 "testing/internal/testdeps"
707
708 {{if .ImportTest}}
709 {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
710 {{end}}
711 {{if .ImportXtest}}
712 {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
713 {{end}}
714 {{if .Cover}}
715 {{range $i, $p := .Cover.Vars}}
716 _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
717 {{end}}
718 {{end}}
719 )
720
721 var tests = []testing.InternalTest{
722 {{range .Tests}}
723 {"{{.Name}}", {{.Package}}.{{.Name}}},
724 {{end}}
725 }
726
727 var benchmarks = []testing.InternalBenchmark{
728 {{range .Benchmarks}}
729 {"{{.Name}}", {{.Package}}.{{.Name}}},
730 {{end}}
731 }
732
733 var fuzzTargets = []testing.InternalFuzzTarget{
734 {{range .FuzzTargets}}
735 {"{{.Name}}", {{.Package}}.{{.Name}}},
736 {{end}}
737 }
738
739 var examples = []testing.InternalExample{
740 {{range .Examples}}
741 {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
742 {{end}}
743 }
744
745 func init() {
746 testdeps.ImportPath = {{.ImportPath | printf "%q"}}
747 }
748
749 {{if .Cover}}
750
751 // Only updated by init functions, so no need for atomicity.
752 var (
753 coverCounters = make(map[string][]uint32)
754 coverBlocks = make(map[string][]testing.CoverBlock)
755 )
756
757 func init() {
758 {{range $i, $p := .Cover.Vars}}
759 {{range $file, $cover := $p.Vars}}
760 coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
761 {{end}}
762 {{end}}
763 }
764
765 func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
766 if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
767 panic("coverage: mismatched sizes")
768 }
769 if coverCounters[fileName] != nil {
770 // Already registered.
771 return
772 }
773 coverCounters[fileName] = counter
774 block := make([]testing.CoverBlock, len(counter))
775 for i := range counter {
776 block[i] = testing.CoverBlock{
777 Line0: pos[3*i+0],
778 Col0: uint16(pos[3*i+2]),
779 Line1: pos[3*i+1],
780 Col1: uint16(pos[3*i+2]>>16),
781 Stmts: numStmts[i],
782 }
783 }
784 coverBlocks[fileName] = block
785 }
786 {{end}}
787
788 func main() {
789 {{if .Cover}}
790 testing.RegisterCover(testing.Cover{
791 Mode: {{printf "%q" .Cover.Mode}},
792 Counters: coverCounters,
793 Blocks: coverBlocks,
794 CoveredPackages: {{printf "%q" .Covered}},
795 })
796 {{end}}
797 m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
798 {{with .TestMain}}
799 {{.Package}}.{{.Name}}(m)
800 os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
801 {{else}}
802 os.Exit(m.Run())
803 {{end}}
804 }
805
806 `)
807
View as plain text