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