Code Coverage for System Tests

I followed the elastic beats blog for finding coverage for the system tests. However my coverage.cov is not getting generated when i run the binary. Need help.

Could you provide a Short, Self Contained, Correct (Compilable), Example and show the exact commands you are using to compile and test. Then maybe one of us can spot a problem.

This is the content inside main_test.go:

 package main
// This file is mandatory as otherwise the filebeat.test binary is not generated correctly.
import (
  "testing"
  "flag"
)
var systemTest *bool
func init() {
  systemTest = flag.Bool("systemTest", true, "Set to true when running system tests")
}
// Test started when the test binary is started. Only calls main.
func TestSystem(t *testing.T) {
  if *systemTest {
     main()
  }
}

The command i use to create a binary is:

go test -c -covermode=count -coverpkg ./...

The command i use to run the binary:

./binary.test -systemTest -test.coverprofile coverage.cov

When i run it, there are some errors which i get(in the binary output) but it keeps running and i'm able to hit the APIs as well. When i stop the binary and see for the coverage file, it is not generated.

Also, i get the following warning when i try to build a test binary

warning: no packages being tested depend on /path/to/packages. Does this have an impact on the resulting binary?

Can you print a message after main() returns in the TestSystem() function and check if that gets logged. The test case must return normally (i.e. main() cannot exit) in order for the coverage file to be written.

func TestSystem(t *testing.T) {
  if *systemTest {
     main()
     t.Log("main returned")
  }
}

You can ignore these warnings. Which golang version are you using?

I tried logging a message but i'm not able to see it. I would also like to tell you that i exit from binary using Ctrl-C after performing the tests. But i don't find the coverage file. I also keep checking the directory where it's supposed to be generated while the tests are running. The file is not generated. I have also fixed the errors which i used to get while running the binary.

Go version which i'm using is : 1.6.2

Can you check what the exit code is after you hit CTRL-C? It should be 0 as @andrewkroh mentioned as otherwise the report is not generated.

You probably need to install your own signal handler to make the program exit gracefully. Then you should see the log message and have a code coverage file.

Yes i already tried using signal handler and the code snippet looks like this.

func TestSystem(t *testing.T) {
if *systemTest {
term := make(chan os.Signal, 1)
signal.Notify(term, os.Interrupt, syscall.SIGTERM)

    go func() {
     select {
            case <-term:
              log.Println("==> Stopping services")
              runtime.GC()
              os.Exit(0)
            }
    }()

    main()

}
}

When i hit ctrl-c, i get this: 2016/11/22 11:50:24 ==> Stopping services and the binary stops. But still the coverage file is not generated.

Instead of using os.Exit(0) try to just use <-term as a blocking channel in main and it will naturally finish the program when the channel is closed. As far as I remember os.Exit(0) does not work as it exits directly from this point and will not execute the additional code which was added to the binary.

Could you please show the same(using <-term) using the code snippet which i shared above ?

I would need to see your main() to advise. In general you need to architect your main() function to return when finished and not call os.Exit(). This means that when the interrupt signal is received that it should stop doing what ever work it's doing and return.

Thanks @andrewkroh, @ruflin for your support. I am now able to generate coverage for my system tests :slight_smile:

@imharishd Glad to hear. It would be nice if you could share in 2-3 sentence with the community what the final solution to your problem was, or where the problem was.

we call beego.Run in our main function. I used signal handler to return from main and also placed the beego.Run inside a go routine. This did the job for me. :slight_smile:

I was able to generate two coverage files and merge them. I am not able to understand the final coverage file that is generated. Please find below the results.

Coverage1: (71.4%)

Coverage2: (72.5%)

FinalSystemCoverage: (72%)

Can someone please interpret the above results and tell how its calculating the final system coverage ? And also, the reason why the covered lines in coverage1 doesn't appear as covered in final coverage ?

How did you merge the two?

We use this: https://github.com/elastic/beats/blob/master/dev-tools/aggregate_coverage.py

mkdir coverage
echo 'mode: count' > ./coverage/system.cov
tail -q -n +2 ./coverage/*.cov >> ./coverage/system.cov

This way i merged the cov files. The above method was listed in the blog. ( https://www.elastic.co/blog/code-coverage-for-your-golang-system-tests)