-cover set,count,atomic
TODO: This feature is not yet fully implemented.
- TODO: Must run with -v to see output.
- TODO: Need control over output format,
Set the mode for coverage analysis for the package[s] being tested.
The default is to do none.
The values:
count: integer: how many times does this statement execute?
atomic: integer: like count, but correct in multithreaded tests;
significantly more expensive.
+ Sets -v. TODO: This will change.
+
+ -coverprofile cover.out
+ Write a coverage profile to the specified file after all tests
+ have passed.
-cpu 1,2,4
Specify a list of GOMAXPROCS values for which the tests or
if testCover != "" {
p.coverMode = testCover
- p.coverVars = declareCoverVars(p.GoFiles...)
+ p.coverVars = declareCoverVars(p.ImportPath, p.GoFiles...)
}
if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), p, p.coverVars); err != nil {
// declareCoverVars attaches the required cover variables names
// to the files, to be used when annotating the files.
-func declareCoverVars(files ...string) map[string]*CoverVar {
+func declareCoverVars(importPath string, files ...string) map[string]*CoverVar {
coverVars := make(map[string]*CoverVar)
for _, file := range files {
coverVars[file] = &CoverVar{
- File: file,
+ File: filepath.Join(importPath, file),
Var: fmt.Sprintf("GoCover_%d", coverIndex),
}
coverIndex++
{{if .NeedXtest}}
_xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
{{end}}
-{{if .CoverEnabled}}
- _fmt "fmt"
-{{end}}
)
var tests = []testing.InternalTest{
}
{{if .CoverEnabled}}
-type coverBlock struct {
- line0 uint32
- col0 uint16
- line1 uint32
- col1 uint16
-}
// Only updated by init functions, so no need for atomicity.
var (
coverCounters = make(map[string][]uint32)
- coverBlocks = make(map[string][]coverBlock)
+ coverBlocks = make(map[string][]testing.CoverBlock)
)
func init() {
{{range $file, $cover := .CoverVars}}
- coverRegisterFile({{printf "%q" $file}}, _test.{{$cover.Var}}.Count[:], _test.{{$cover.Var}}.Pos[:]...)
+ coverRegisterFile({{printf "%q" $cover.File}}, _test.{{$cover.Var}}.Count[:], _test.{{$cover.Var}}.Pos[:], _test.{{$cover.Var}}.NumStmt[:])
{{end}}
}
-func coverRegisterFile(fileName string, counter []uint32, pos ...uint32) {
- if 3*len(counter) != len(pos) {
+func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
+ if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
panic("coverage: mismatched sizes")
}
if coverCounters[fileName] != nil {
panic("coverage: duplicate counter array for " + fileName)
}
coverCounters[fileName] = counter
- block := make([]coverBlock, len(counter))
+ block := make([]testing.CoverBlock, len(counter))
for i := range counter {
- block[i] = coverBlock{
- line0: pos[3*i+0],
- col0: uint16(pos[3*i+2]),
- line1: pos[3*i+1],
- col1: uint16(pos[3*i+2]>>16),
+ block[i] = testing.CoverBlock{
+ Line0: pos[3*i+0],
+ Col0: uint16(pos[3*i+2]),
+ Line1: pos[3*i+1],
+ Col1: uint16(pos[3*i+2]>>16),
+ Stmts: numStmts[i],
}
}
coverBlocks[fileName] = block
}
-
-func coverDump() {
- for name, counts := range coverCounters {
- blocks := coverBlocks[name]
- for i, count := range counts {
- _, err := _fmt.Printf("%s:%d.%d,%d.%d %d\n", name,
- blocks[i].line0, blocks[i].col0,
- blocks[i].line1, blocks[i].col1,
- count)
- if err != nil {
- panic(err)
- }
- }
- }
-}
{{end}}
func main() {
- testing.Main(matchString, tests, benchmarks, examples)
{{if .CoverEnabled}}
- coverDump()
+ testing.RegisterCover(coverCounters, coverBlocks)
{{end}}
+ testing.Main(matchString, tests, benchmarks, examples)
}
`))
-bench="": passes -test.bench to test
-benchmem=false: print memory allocation statistics for benchmarks
-benchtime=1s: passes -test.benchtime to test
+ -cover="": passes -test.cover to test
+ -coverprofile="": passes -test.coverprofile to test
-cpu="": passes -test.cpu to test
-cpuprofile="": passes -test.cpuprofile to test
-memprofile="": passes -test.memprofile to test
{name: "c", boolVar: &testC},
{name: "file", multiOK: true},
{name: "i", boolVar: &testI},
- {name: "cover"},
// build flags.
{name: "a", boolVar: &buildA},
{name: "bench", passToTest: true},
{name: "benchmem", boolVar: new(bool), passToTest: true},
{name: "benchtime", passToTest: true},
+ {name: "cover", passToTest: true},
+ {name: "coverprofile", passToTest: true},
{name: "cpu", passToTest: true},
{name: "cpuprofile", passToTest: true},
{name: "memprofile", passToTest: true},
testBench = true
case "timeout":
testTimeout = value
- case "blockprofile", "cpuprofile", "memprofile":
+ case "blockprofile", "coverprofile", "cpuprofile", "memprofile":
testProfile = true
case "outputdir":
outputDir = value
default:
fatalf("invalid flag argument for -cover: %q", value)
}
+ // Guarantee we see the coverage statistics. Doesn't turn -v on generally; tricky. TODO?
+ testV = true
}
if extraWord {
i++
--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Support for test coverage.
+
+package testing
+
+import (
+ "fmt"
+ "os"
+)
+
+// CoverBlock records the coverage data for a single basic block.
+// NOTE: This struct is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+type CoverBlock struct {
+ Line0 uint32
+ Col0 uint16
+ Line1 uint32
+ Col1 uint16
+ Stmts uint16
+}
+
+var (
+ coverCounters map[string][]uint32
+ coverBlocks map[string][]CoverBlock
+)
+
+// RegisterCover records the coverage data accumulators for the tests.
+// NOTE: This struct is internal to the testing infrastructure and may change.
+// It is not covered (yet) by the Go 1 compatibility guidelines.
+func RegisterCover(c map[string][]uint32, b map[string][]CoverBlock) {
+ coverCounters = c
+ coverBlocks = b
+}
+
+// mustBeNil checks the error and, if present, reports it and exits.
+func mustBeNil(err error) {
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s\n", err)
+ os.Exit(2)
+ }
+}
+
+// coverReport reports the coverage percentage and writes a coverage profile if requested.
+func coverReport() {
+ var f *os.File
+ var err error
+ if *coverProfile != "" {
+ f, err = os.Create(toOutputDir(*coverProfile))
+ mustBeNil(err)
+ defer func() { mustBeNil(f.Close()) }()
+ }
+
+ var active, total int64
+ packageName := ""
+ for name, counts := range coverCounters {
+ if packageName == "" {
+ // Package name ends at last slash.
+ for i, c := range name {
+ if c == '/' {
+ packageName = name[:i]
+ }
+ }
+ }
+ blocks := coverBlocks[name]
+ for i, count := range counts {
+ stmts := int64(blocks[i].Stmts)
+ total += stmts
+ if count > 0 {
+ active += stmts
+ }
+ if f != nil {
+ _, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name,
+ blocks[i].Line0, blocks[i].Col0,
+ blocks[i].Line1, blocks[i].Col1,
+ stmts,
+ count)
+ mustBeNil(err)
+ }
+ }
+ }
+ if total == 0 {
+ total = 1
+ }
+ if packageName == "" {
+ packageName = "package"
+ }
+ fmt.Printf("test coverage for %s: %.1f%% of statements\n", packageName, 100*float64(active)/float64(total))
+}
// Report as tests are run; default is silent for success.
chatty = flag.Bool("test.v", false, "verbose: print additional output")
+ cover = flag.String("test.cover", "", "cover mode: set, count, atomic; default is none")
+ coverProfile = flag.String("test.coverprofile", "", "write a coveraage profile to the named file after execution")
match = flag.String("test.run", "", "regular expression to select tests and examples to run")
memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution")
memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate")
}
f.Close()
}
+ if *cover != "" {
+ coverReport()
+ }
}
// toOutputDir returns the file name relocated, if required, to outputDir.