// 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" "internal/goexperiment" "os" "sync/atomic" ) // CoverBlock records the coverage data for a single basic block. // The fields are 1-indexed, as in an editor: The opening line of // the file is number 1, for example. Columns are measured // in bytes. // 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 // Line number for block start. Col0 uint16 // Column number for block start. Line1 uint32 // Line number for block end. Col1 uint16 // Column number for block end. Stmts uint16 // Number of statements included in this block. } var cover Cover // Cover records information about test coverage checking. // NOTE: This struct is internal to the testing infrastructure and may change. // It is not covered (yet) by the Go 1 compatibility guidelines. type Cover struct { Mode string Counters map[string][]uint32 Blocks map[string][]CoverBlock CoveredPackages string } // Coverage reports the current code coverage as a fraction in the range [0, 1]. // If coverage is not enabled, Coverage returns 0. // // When running a large set of sequential test cases, checking Coverage after each one // can be useful for identifying which test cases exercise new code paths. // It is not a replacement for the reports generated by 'go test -cover' and // 'go tool cover'. func Coverage() float64 { if goexperiment.CoverageRedesign { return coverage2() } var n, d int64 for _, counters := range cover.Counters { for i := range counters { if atomic.LoadUint32(&counters[i]) > 0 { n++ } d++ } } if d == 0 { return 0 } return float64(n) / float64(d) } // RegisterCover records the coverage data accumulators for the tests. // NOTE: This function is internal to the testing infrastructure and may change. // It is not covered (yet) by the Go 1 compatibility guidelines. func RegisterCover(c Cover) { cover = c } // 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() { if goexperiment.CoverageRedesign { coverReport2() return } var f *os.File var err error if *coverProfile != "" { f, err = os.Create(toOutputDir(*coverProfile)) mustBeNil(err) fmt.Fprintf(f, "mode: %s\n", cover.Mode) defer func() { mustBeNil(f.Close()) }() } var active, total int64 var count uint32 for name, counts := range cover.Counters { blocks := cover.Blocks[name] for i := range counts { stmts := int64(blocks[i].Stmts) total += stmts count = atomic.LoadUint32(&counts[i]) // For -mode=atomic. 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 { fmt.Println("coverage: [no statements]") return } fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages) }