Introducing gocov
A couple of weeks ago I announced gocov, a coverage testing tool for the Go programming language. I wrote gocov to quickly get an idea of how broadly tested packages are (namely exp/types, which I'm working on in the background). The tool itself is written in Go, and works by source instrumentation/transformation. Currently gocov only does statement coverage.Using gocov is relatively simple (if I do say so myself). First, you install gocov by running:
go get github.com/axw/gocov/gocov
This will install the gocov tool into your $GOPATH/bin directory. Once you have it installed, you can test a package (i.e. run its tests, and generate coverage data), by running:
gocov test <path/to/package>
Under the covers, this will run "go test <path/to/package>", after having gone through the process of instrumenting the source. Once the tests are complete, gocov will output the coverage information as a JSON structure to stdout. So you might want to pipe that output somewhere...
Once you've got the coverage information, you'll probably want to view it. So there are two other gocov commands: report, and annotate. The report command will generate a text report of the coverage of all the functions in the coverage information provided to it. For example:
gocov test github.com/axw/llgo/types | gocov report
... will generate a report that looks something like:
...
types/exportdata.go readGopackHeader 69.23% (9/13)
types/gcimporter.go gcParser.expect 66.67% (4/6)
types/gcimporter.go gcParser.expectKeyword 66.67% (2/3)
...
The annotate command will print out the source for a specified function, along with an annotation for each line that was missed. For example:gocov test github.com/axw/llgo/types | gocov annotate - types.gcParser.expectKeyword
... will output the following:
266 func (p *gcParser) expectKeyword(keyword string) {
267 lit := p.expect(scanner.Ident)
268 if lit != keyword {
269 MISS p.errorf("expected keyword %s, got %q", keyword, lit)
270 }
271 }
As is often the case when I write software, I wrote gocov for my own needs; as such it's not terribly featureful, only doing what I've needed thus far. If you would like to add a feature (maybe HTML output, or branch coverage), feel free to send a pull request on the Github repository, and I'll take a gander.
Anyway, I hope it's of use to people. But not too many people, I don't have time to fix all of my crappy code! (Just kidding, I have no life.)
Update on llgo: interface comparisons, exp/types
I don't have a lot to report on this front, as I've been doing various other things, like that stuff up there, but I can share a couple of bits of mildly interesting news.I've been working a little on the runtime for llgo, and I'm proud to say there's now an initial implementation of interface comparison in the runtime. This involved filling in the algorithm table for runtime types, implementing the runtime equality function (runtime.memequal), and implementing a runtime function (runtime.compareI2I) to extract and call it. It probably doesn't sound exciting when put like that, but this is something of a milestone.
By the way, if you want to actually use the runtime, you can do it like this:
- Compile your program with llgo, storing the bitcode in file x.ll.
- Compile llgo/runtime/*.go with llgo, storing the bitcode in file y.ll.
- Link the two together, using llvm-link: llvm-link -o z.ll x.ll y.ll
And you're done. The resultant module, housed in z.ll, contains your program and the llgo runtime. Now you can concatenate strings and compare interfaces to your heart's content. Eventually llgo will contain an integrated linker, which will rewrite symbol names according to package paths.
Finally, on exp/types: I submitted my first two CL's. Some of my ideas for exp/types were ill thought out, so the first was rejected (fairly), and the second needs some rework. I'll be writing up a design proposal document at some stage, to better document my rationale for changes. Anyway, I'll keep plugging away...
Ade!