uartois / sonar-golang

Sonarqube plugin for the golang language.
GNU Lesser General Public License v3.0
245 stars 32 forks source link

Question: Single vs Multiple coverage.xml files #34

Open kenjones-cisco opened 7 years ago

kenjones-cisco commented 7 years ago

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>
  1. 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?

  2. 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
    ...
thibaultfalque commented 7 years ago

Hi,

Thanks for your questions.

  1. The plugin use the report generated by gocov-xml. We used gocov to convert a coverage profile generate by go test. But we can't launch go test for all packages in one time (if you have a any idea or solution for do that,...). So we need to launch go test and generate the coverage profile and send this profile to gocov for convert and the conversion to gocov-xml.

  2. 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.

kenjones-cisco commented 7 years ago

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.

danielleberre commented 7 years ago

@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?

kenjones-cisco commented 7 years ago

Sure, no problem.

kenjones-cisco commented 7 years ago

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
danielleberre commented 6 years ago

@thibaultfalque are we done here?

thibaultfalque commented 6 years ago

See the PR by @Kernle32DLL #50

kernle32dll commented 6 years ago

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).

thibaultfalque commented 6 years ago

Thanks @Kernle32DLL for your explanation