| // 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" |
| "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 { |
| 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() { |
| 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) |
| } |