sonar-golang
sonar-golang copied to clipboard
Question: Single vs Multiple coverage.xml files
Description
In the README I noticed the following section:
You must end-up with one coverage file per directory:
pkg1/coverage.xml
pkg2/coverage.xml
pkg3/coverage.xml
...
Currently I'm able to generate a single coverage.xml file for an entire project with multiple packages within the same project. And cobertura within Jenkins is able to read and process the file and show all of the packages.
Example single coverage.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-03.dtd">
<coverage line-rate="0" branch-rate="0" version="" timestamp="1506622955562">
<sources>
<source>/usr/local/go/src</source>
<source>/go/src</source>
<source>/authlib/src</source>
</sources>
<packages>
<package name="gitscm.cisco.com/ccdev/authlib/authz" line-rate="0" branch-rate="0" complexity="0">
<classes>
<class name="-" filename="gitscm.cisco.com/ccdev/authlib/authz/context.go" line-rate="0" branch-rate="0" complexity="0">
<methods>
<method name="key" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="11" hits="11"></line>
</lines>
</method>
<method name="GetUsername" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="16" hits="6"></line>
<line number="17" hits="5"></line>
<line number="19" hits="1"></line>
</lines>
</method>
<method name="SetUsername" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="24" hits="5"></line>
</lines>
</method>
</methods>
<lines>
<line number="11" hits="11"></line>
<line number="16" hits="6"></line>
<line number="17" hits="5"></line>
<line number="19" hits="1"></line>
<line number="24" hits="5"></line>
</lines>
</class>
</classes>
</package>
<package name="gitscm.cisco.com/ccdev/authlib/authz/casbin" line-rate="0" branch-rate="0" complexity="0">
<classes>
<class name="-" filename="gitscm.cisco.com/ccdev/authlib/authz/casbin/ldap_role_manager.go" line-rate="0" branch-rate="0" complexity="0">
<methods>
<method name="CheckLdapEnv" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="32" hits="4"></line>
<line number="33" hits="1"></line>
<line number="35" hits="3"></line>
<line number="36" hits="1"></line>
<line number="38" hits="2"></line>
<line number="39" hits="1"></line>
<line number="41" hits="1"></line>
</lines>
</method>
<method name="LDAPRoleManager" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="46" hits="1"></line>
</lines>
</method>
<method name="@46:9" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="47" hits="1"></line>
<line number="48" hits="1"></line>
<line number="49" hits="1"></line>
<line number="50" hits="1"></line>
<line number="53" hits="1"></line>
<line number="54" hits="1"></line>
<line number="56" hits="1"></line>
</lines>
</method>
<method name="NewLdapRoleManager" signature="" line-rate="0" branch-rate="0">
<lines>
<line number="62" hits="2"></line>
<line number="70" hits="2"></line>
<line number="71" hits="2"></line>
<line number="72" hits="1"></line>
<line number="74" hits="1"></line>
<line number="78" hits="1"></line>
</lines>
</method>
</methods>
<lines>
<line number="32" hits="4"></line>
<line number="33" hits="1"></line>
<line number="35" hits="3"></line>
<line number="36" hits="1"></line>
<line number="38" hits="2"></line>
<line number="39" hits="1"></line>
<line number="41" hits="1"></line>
<line number="46" hits="1"></line>
<line number="47" hits="1"></line>
<line number="48" hits="1"></line>
<line number="49" hits="1"></line>
<line number="50" hits="1"></line>
<line number="53" hits="1"></line>
<line number="54" hits="1"></line>
<line number="56" hits="1"></line>
<line number="62" hits="2"></line>
<line number="70" hits="2"></line>
<line number="71" hits="2"></line>
<line number="72" hits="1"></line>
<line number="74" hits="1"></line>
<line number="78" hits="1"></line>
</lines>
</class>
</classes>
</package>
</packages>
</coverage>
-
Is there a technical limitation within SonarQube that requires the coverage to be done as one coverage.xml per package? Or is this rule imposed by the plugin?
-
As a follow up, does the coverage.xml have to be in the same directory as the code? Or is it possible to leverage a
cover
directory that has a directory for each package and the coverage.xml file exists within that directory?
Example Directory structure for coverage:
cover/
github.com/org/repo/pkg1/coverage.xml
github.com/org/repo/pkg2/coverage.xml
github.com/org/repo/pkg3/coverage.xml
...
Hi,
Thanks for your questions.
-
The plugin use the report generated by
gocov-xml
. We usedgocov
to convert a coverage profile generate by go test. But we can't launchgo test
for all packages in one time (if you have a any idea or solution for do that,...). So we need to launchgo test
and generate the coverage profile and send this profile togocov
for convert and the conversion togocov-xml
. -
It's not a problem. I used Files.walk for explore the project and visit directories.
How did you do for generate a single coverage file ? If it's possible I will can modify the plugin to use a single coverage file.
Generating single coverage.xml
workdir=cover
profile="$workdir/cover.out"
mode=count
for pkg in $(glide nv);
do
for subpkg in $(go list "${pkg}");
do
f="$workdir/$(echo "$subpkg" | tr / -).cover"
go test -v -covermode="$mode" -coverprofile="$f" "$subpkg" >> test.out
done
done
set -- "$workdir"/*.cover
if [ ! -f "$1" ]; then
echo "No Test Cases"; exit 0
fi
echo "mode: $mode" >"$profile"
grep -h -v "^mode:" "$workdir"/*.cover >>"$profile"
rm -f test.xml coverage.xml
go2xunit -input test.out -output test.xml
gocov convert "$profile" | gocov-xml > coverage.xml
That is how I have it scripted to generate the test.xml
and coverage.xml
that I have been using.
My alternate approach would be just to write the coverage.xml files into the cover/ directory as I don't like the idea of writing any files to my actual package directories.
@thibaultfalque I guess that making sure to search first a single cover file in the root directory else look for multiple cover files in subdirectories could do the trick.
@kenjones-cisco thanks for the script. Would you agree that we display it in the README page of the script?
Sure, no problem.
FYI, to make it not specific to glide, you should can use this:
workdir=cover
profile="$workdir/cover.out"
mode=count
for pkg in $(go list ./...);
do
f="$workdir/$(echo "$pkg" | tr / -).cover"
go test -v -covermode="$mode" -coverprofile="$f" "$pkg" >> test.out
done
set -- "$workdir"/*.cover
if [ ! -f "$1" ]; then
rm -f "$results" || :
echo "No Test Cases"; exit 0
fi
echo "mode: $mode" >"$profile"
grep -h -v "^mode:" "$workdir"/*.cover >>"$profile"
rm -f test.xml coverage.xml
go2xunit -input test.out -output test.xml
gocov convert "$profile" | gocov-xml > coverage.xml
@thibaultfalque are we done here?
See the PR by @Kernle32DLL #50
For a bit more context what gocov test ./...
does - it internally does exactly that. Its walks trough the folders, and executes go test
, and gocov convert
. The nice thing is, that this method aggregates the results, and creates a single coverage.xml
, without some shell magic (which is good, since this way it can be used across platforms).
Also also - Golang 1.10 will bring support for go test ./... -coverprofile...
, which was not possible so far (hence all the extra hops with executing go test
in each package separately).
Thanks @Kernle32DLL for your explanation