Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get golang coverage programmatically

The go -cover or -coverprofile is great when running go tests and can show nicely in html or plain text. But is there an api to access it programmatically or process the file?

like image 776
cellige Avatar asked Jul 09 '14 03:07

cellige


2 Answers

You can try axw/gocov, which:

  • will run test with a -coverprofile argument
  • parse the results (gocov/convert.go)

You can see a tool like GoCov GUI uses gocov.Statement in convert_statements(s []*gocov.Statement, offset int) in order to build a small GUI:

https://camo.githubusercontent.com/c7865d70f68bcf9ba8a8d82cf40e75b254301be6/687474703a2f2f6e6f736d696c65666163652e72752f696d616765732f676f636f766775692e706e67

like image 156
VonC Avatar answered Oct 12 '22 19:10

VonC


Let us assume that we want to obtain test coverage percentage (as float64) on a project that we dynamically fetch from a git repo from somewhere, and store in the current folder under "_repos/src". The input would be the project in a typical "go get" format. We need to execute "go test -cover", with the properly GOPATH variable set, parse the output, and extract the actual percentage of the test coverage. With the current Go 1.9 testing tool, the code below achieves that.

// ParseFloatPercent turns string with number% into float.
func ParseFloatPercent(s string, bitSize int) (f float64, err error) {
    i := strings.Index(s, "%")
    if i < 0 {
        return 0, fmt.Errorf("ParseFloatPercent: percentage sign not found")
    }
    f, err = strconv.ParseFloat(s[:i], bitSize)
    if err != nil {
        return 0, err
    }
    return f / 100, nil
}

// GetTestCoverage returns the tests code coverage as float
// we are assuming that project is a string
// in a standard "Go get" form, for example:
//     "github.com/apache/kafka"
// and, that you have cloned the repo into "_repos/src"
// of the current folder where your executable is running.
//
func GetTestCoverage(project string) (float64, error) {
    cmdArgs := append([]string{"test", "-cover"}, project)
    cmd := exec.Command("go", cmdArgs...)
    // get the file absolute path of our main executable
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if err != nil {
        log.Println(err)
        return 0, err
    }
    // set the GOPATH for tests to work
    cmd.Env = os.Environ()
    cmd.Env = append(cmd.Env, "GOPATH="+dir+"/_repos/")

    var out []byte
    cmd.Stdin = nil
    out, err = cmd.Output()

    if err != nil {
        fmt.Println(err.Error())
        return 0, err
    }

    r := bufio.NewReader(bytes.NewReader(out))
    // first line from running "go test -cover" should be in a form
    // ok   <project>   6.554s  coverage: 64.9% of statements
    // split with /t and <space> characters
    line, _, err := r.ReadLine()

    if err != nil {
        fmt.Println(err.Error())
        return 0, err
    }

    parts := strings.Split(string(line), " ")
    if len(parts) < 6 {
        return 0, errors.New("go test -cover do not report coverage correctly")
    }
    if parts[0] != "ok" {
        return 0, errors.New("tests do not pass")
    }
    f, err := ParseFloatPercent(parts[3], 64)
    if err != nil {
        // the percentage parsing problem
        return 0, err
    }

    return f, nil
}
like image 35
marni Avatar answered Oct 12 '22 18:10

marni