gocode-coveragego-testinggo-build

Go coverage profile gives a mix of module url and local path


I recently started playing with go build -cover -o to collect the code coverage for integration test in Go 1.20. I used the textfmt to get the coverage, but looking at the text file, it has a local path for the file that I built the executable for, and a module url for the utility function which resides in the same project. Below is an example:

./main/program.go

package main

import "example.com/m/util"

func main() {
    util.PrintFromUtil()
}

./util/util.go

package util

import "fmt"

func PrintFromUtil() {
    fmt.Println("This is from package util")
}

./go.mod

module example.com/m

go 1.20

This are the commands I used for getting the coverage profile:

$ go build -cover -o ./main/program ./main/program.go
$ GOCOVERDIR=coverage ./main/program
This is from package util
$ go tool covdata textfmt -i=coverage -o profile.txt
$ cat profile.txt 
mode: set
/MYLOCALPATH/main/program1.go:5.13,7.2 1 1
example.com/m/util/util.go:5.22,7.2 1 1

The issue seems to be the import. One easy fix is just to clean up the profile.txt manually by replacing the local path with the project url or vice versa. However, is there any cleaner way to do this? Since it is meant for integration tests, I can imagine there should be plenty of imports in the tests. Any help is appreciated.


Solution

  • Digging deeper:

    The issue is that you specify a file when building the binary, not a package. It goes away when you change

    go build -cover -o ./main/program ./main/program.go
    

    to

    go build -cover -o ./main/program ./main
    

    On the other hand, main seems to be a problematic path name.

    May I suggest that you restructure your source for example to cmd/program

    mkdir cmd
    mv main cmd/program
    

    and then use

    go build -cover ./cmd/program
    ./program
    

    instead?


    Old answer:

    It seems you are not using Go modules (no go.mod file) and working outside the $GOPATH, so this is the import path for your main function.

    How about putting your code inside the example.com/m module? You can also make a new module and combine multiple modules inside a workspace if you want to further organize your source - or work inside the GOPATH. Most people choose the first option, since it's the most simple one, but your use case might be different.