From 5421bc5df12333856733be01895b8ed80c67ce00 Mon Sep 17 00:00:00 2001 From: Camillo Facello <52419977+camillof@users.noreply.github.com> Date: Fri, 24 Feb 2023 16:22:55 -0300 Subject: [PATCH 1/6] Add dotcover formatter and tests --- Makefile | 3 ++ cmd/format-coverage.go | 4 +- formatters/dotcover/dotcover.go | 77 +++++++++++++++++++++++++++ formatters/dotcover/dotcover_test.go | 42 +++++++++++++++ formatters/dotcover/example.xml | 56 +++++++++++++++++++ formatters/dotcover/xml.go | 15 ++++++ integration-tests/dotcover/Dockerfile | 40 ++++++++++++++ 7 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 formatters/dotcover/dotcover.go create mode 100644 formatters/dotcover/dotcover_test.go create mode 100644 formatters/dotcover/example.xml create mode 100644 formatters/dotcover/xml.go create mode 100644 integration-tests/dotcover/Dockerfile diff --git a/Makefile b/Makefile index 181039e0..b4c5d62f 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,9 @@ test-cobertura: test-excoveralls: docker build -f integration-tests/excoveralls/Dockerfile . +test-dotcover: + docker build -f integration-tests/dotcover/Dockerfile . + publish-head: $(call upload_artifacts,head) diff --git a/cmd/format-coverage.go b/cmd/format-coverage.go index 32de101c..c65cbe2d 100644 --- a/cmd/format-coverage.go +++ b/cmd/format-coverage.go @@ -19,6 +19,7 @@ import ( "github.com/codeclimate/test-reporter/formatters/lcovjson" "github.com/codeclimate/test-reporter/formatters/simplecov" "github.com/codeclimate/test-reporter/formatters/xccov" + "github.com/codeclimate/test-reporter/formatters/dotcover" "github.com/gobuffalo/envy" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -37,7 +38,7 @@ type CoverageFormatter struct { var formatOptions = CoverageFormatter{} // a prioritized list of the formatters to use -var formatterList = []string{"clover", "cobertura", "coverage.py", "excoveralls", "gcov", "gocov", "jacoco", "lcov", "lcov-json", "simplecov", "xccov"} +var formatterList = []string{"clover", "cobertura", "coverage.py", "excoveralls", "gcov", "gocov", "jacoco", "lcov", "lcov-json", "simplecov", "xccov", "dotcover"} // a map of the formatters to use var formatterMap = map[string]formatters.Formatter{ @@ -52,6 +53,7 @@ var formatterMap = map[string]formatters.Formatter{ "lcov-json": &lcovjson.Formatter{}, "simplecov": &simplecov.Formatter{}, "xccov": &xccov.Formatter{}, + "dotcover": &dotcover.Formatter{}, } // formatCoverageCmd represents the format command diff --git a/formatters/dotcover/dotcover.go b/formatters/dotcover/dotcover.go new file mode 100644 index 00000000..1e2525f7 --- /dev/null +++ b/formatters/dotcover/dotcover.go @@ -0,0 +1,77 @@ +package dotcover + +import ( + "encoding/xml" + "os" + "strings" + + "github.com/Sirupsen/logrus" + "github.com/codeclimate/test-reporter/env" + "github.com/codeclimate/test-reporter/formatters" + "github.com/pkg/errors" +) + +var searchPaths = []string{"dotcover.xml"} + +type Formatter struct { + Path string +} + +func (f *Formatter) Search(paths ...string) (string, error) { + paths = append(paths, searchPaths...) + for _, p := range paths { + logrus.Debugf("checking search path %s for dotcover formatter", p) + if _, err := os.Stat(p); err == nil { + f.Path = p + return p, nil + } + } + + return "", errors.WithStack(errors.Errorf("could not find any files in search paths for dotcover. search paths were: %s", strings.Join(paths, ", "))) +} + + +func (r Formatter) Format() (formatters.Report, error) { + rep, err := formatters.NewReport() + if err != nil { + return rep, err + } + + fx, err := os.Open(r.Path) + if err != nil { + return rep, errors.WithStack(err) + } + + c := &xmlDotCover{} + err = xml.NewDecoder(fx).Decode(c) + if err != nil { + return rep, errors.WithStack(err) + } + + gitHead, _ := env.GetHead() + + for _, file := range c.Files { + sf, err := formatters.NewSourceFile(file.Path, gitHead) + if err != nil { + return rep, errors.WithStack(err) + } + + for _, statement := range c.Statements { + if file.Index == statement.FileIndex { + if statement.Covered { + sf.Coverage = append(sf.Coverage, formatters.NewNullInt(1)) + } else { + sf.Coverage = append(sf.Coverage, formatters.NewNullInt(0)) + } + } + } + + err = rep.AddSourceFile(sf) + + if err != nil { + return rep, errors.WithStack(err) + } + } + + return rep, nil +} diff --git a/formatters/dotcover/dotcover_test.go b/formatters/dotcover/dotcover_test.go new file mode 100644 index 00000000..bb7f809f --- /dev/null +++ b/formatters/dotcover/dotcover_test.go @@ -0,0 +1,42 @@ +package dotcover + +import ( + "testing" + + "gopkg.in/src-d/go-git.v4/plumbing/object" + + "github.com/codeclimate/test-reporter/env" + "github.com/stretchr/testify/require" +) + +func Test_Parse(t *testing.T) { + ogb := env.GitBlob + defer func() { + env.GitBlob = ogb + }() + env.GitBlob = func(s string, c *object.Commit) (string, error) { + return s, nil + } + + assert := require.New(t) + + formatter := Formatter{ + Path: "./example.xml", + } + rep, err := formatter.Format() + assert.NoError(err) + + assert.Len(rep.SourceFiles, 3) + assert.InDelta(71, rep.CoveredPercent, 1) + assert.Equal(24, rep.LineCounts.Total) + assert.Equal(17, rep.LineCounts.Covered) + + sf_one := rep.SourceFiles[`C:\Users\fulano\Desktop\unit-testing-using-mstest\PrimeService\PrimeService.cs`] + assert.InDelta(83, sf_one.CoveredPercent, 1) + + sf_two := rep.SourceFiles[`C:\Users\fulano\Desktop\unit-testing-using-mstest\PrimeService\SecondService.cs`] + assert.Equal(0.0, sf_two.CoveredPercent) + + sf_three := rep.SourceFiles[`C:\Users\fulano\Desktop\unit-testing-using-mstest\PrimeService.Tests\PrimeService_IsPrimeShould.cs`] + assert.Equal(100.0, sf_three.CoveredPercent) +} diff --git a/formatters/dotcover/example.xml b/formatters/dotcover/example.xml new file mode 100644 index 00000000..f4eff90f --- /dev/null +++ b/formatters/dotcover/example.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/formatters/dotcover/xml.go b/formatters/dotcover/xml.go new file mode 100644 index 00000000..8911a437 --- /dev/null +++ b/formatters/dotcover/xml.go @@ -0,0 +1,15 @@ +package dotcover + +import "encoding/xml" + +type xmlDotCover struct { + XMLName xml.Name `xml:"Root"` + Files []struct { + Path string `xml:"Name,attr"` + Index int `xml:"Index,attr"` + } `xml:"FileIndices>File"` + Statements []struct { + FileIndex int `xml:"FileIndex,attr"` + Covered bool `xml:"Covered,attr"` + } `xml:"Assembly>Namespace>Type>Method>Statement"` +} diff --git a/integration-tests/dotcover/Dockerfile b/integration-tests/dotcover/Dockerfile new file mode 100644 index 00000000..d5f591d4 --- /dev/null +++ b/integration-tests/dotcover/Dockerfile @@ -0,0 +1,40 @@ +FROM mcr.microsoft.com/dotnet/sdk:7.0 + +# Install GoLang +RUN curl -O https://dl.google.com/go/go1.15.linux-amd64.tar.gz +RUN tar -xzf go1.15.linux-amd64.tar.gz +RUN mv go /usr/local + +ENV PATH $PATH:/usr/local/go/bin +ENV GOBIN="/usr/local/go/bin" +RUN go version + +ENV GOPATH /go +RUN mkdir $GOPATH +ENV PATH $PATH:/go/bin + +ENV CCTR=$GOPATH/src/github.com/codeclimate/test-reporter +RUN mkdir -p $CCTR +WORKDIR $CCTR +COPY . . +RUN go install -v + +ENV PATH $PATH:/root/.dotnet/tools +RUN dotnet tool install JetBrains.dotCover.GlobalTool -g --version "2022.3.2" + +# Clone .NET example repo and run test +RUN git clone https://github.com/codeclimate/dot-net-coverage-test.git +WORKDIR dot-net-coverage-test +RUN dotnet build +RUN dotnet dotcover test --dcReportType=DetailedXML --dcOutput="dotcover.xml" --no-build + +RUN echo "testing" > ignore.me && \ + git config --global user.email "you@example.com" && \ + git config --global user.name "Your Name" && \ + git add ignore.me && \ + git commit -m "testing" + +ENV CC_TEST_REPORTER_ID=49a59a1849364524250d544e098b5d987335376cdb739ea7649c9f8bce968e3b +RUN test-reporter format-coverage -d -t dotcover +RUN cat coverage/codeclimate.json +RUN test-reporter upload-coverage -d -s 2 From e332639e49df456038bce62770557acadb279d84 Mon Sep 17 00:00:00 2001 From: Camillo Facello <52419977+camillof@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:08:13 -0300 Subject: [PATCH 2/6] Update man page --- man/cc-test-reporter-format-coverage.1.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/man/cc-test-reporter-format-coverage.1.md b/man/cc-test-reporter-format-coverage.1.md index 6dbae790..ebf5e8ed 100644 --- a/man/cc-test-reporter-format-coverage.1.md +++ b/man/cc-test-reporter-format-coverage.1.md @@ -17,7 +17,7 @@ Locate, parse, and re-format supported coverage sources. # OPTIONS -## -t, --input-type *simplecov*|*lcov*|*coverage.py*|*gcov*|*clover* +## -t, --input-type *simplecov*|*lcov*|*coverage.py*|*gcov*|*clover*|*dotcover* Identifies the input type (format) of the COVERAGE_FILE. @@ -67,6 +67,10 @@ As generated by **phpunit --coverage-clover**. As generated by `go test -coverprofile=c.out` +## ./dotcover.xml *DotCover* + +As generated by `dotnet dotcover test --dcReportType=DetailedXML --dcOutput="dotcover.xml"` + # ENVIRONMENT VARIABLES *GIT_BRANCH*, *GIT_COMMIT_SHA*, and *GIT_COMMITTED_AT* are required. *CI_NAME*, From 521a0ef31ed70bb07a21e887dca8f1c24b60fd95 Mon Sep 17 00:00:00 2001 From: Camillo Facello <52419977+camillof@users.noreply.github.com> Date: Thu, 2 Mar 2023 14:02:08 -0300 Subject: [PATCH 3/6] Fix indentation --- cmd/format-coverage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/format-coverage.go b/cmd/format-coverage.go index c65cbe2d..2d5b906e 100644 --- a/cmd/format-coverage.go +++ b/cmd/format-coverage.go @@ -53,7 +53,7 @@ var formatterMap = map[string]formatters.Formatter{ "lcov-json": &lcovjson.Formatter{}, "simplecov": &simplecov.Formatter{}, "xccov": &xccov.Formatter{}, - "dotcover": &dotcover.Formatter{}, + "dotcover": &dotcover.Formatter{}, } // formatCoverageCmd represents the format command From 666783080e312484afe4d62a6bcf42ea15294717 Mon Sep 17 00:00:00 2001 From: Camillo Facello <52419977+camillof@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:17:59 -0300 Subject: [PATCH 4/6] Fix golint warnings --- formatters/dotcover/dotcover.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/formatters/dotcover/dotcover.go b/formatters/dotcover/dotcover.go index 1e2525f7..3e46c115 100644 --- a/formatters/dotcover/dotcover.go +++ b/formatters/dotcover/dotcover.go @@ -13,10 +13,12 @@ import ( var searchPaths = []string{"dotcover.xml"} +// Formatter is the exported struct to be used on format-coverage.go type Formatter struct { Path string } +// Search looks for the dotcover test report file in default paths or provided ones. func (f *Formatter) Search(paths ...string) (string, error) { paths = append(paths, searchPaths...) for _, p := range paths { @@ -30,14 +32,14 @@ func (f *Formatter) Search(paths ...string) (string, error) { return "", errors.WithStack(errors.Errorf("could not find any files in search paths for dotcover. search paths were: %s", strings.Join(paths, ", "))) } - -func (r Formatter) Format() (formatters.Report, error) { +// Format transforms the provided test report into a CC readable report format. +func (f Formatter) Format() (formatters.Report, error) { rep, err := formatters.NewReport() if err != nil { return rep, err } - fx, err := os.Open(r.Path) + fx, err := os.Open(f.Path) if err != nil { return rep, errors.WithStack(err) } From 9b409b15bf5b10b8a672f1e22d48221b057a369e Mon Sep 17 00:00:00 2001 From: Camillo Facello <52419977+camillof@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:20:39 -0300 Subject: [PATCH 5/6] Fix gofmt warnings --- cmd/format-coverage.go | 2 +- formatters/dotcover/xml.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/format-coverage.go b/cmd/format-coverage.go index 2d5b906e..f8625239 100644 --- a/cmd/format-coverage.go +++ b/cmd/format-coverage.go @@ -11,6 +11,7 @@ import ( "github.com/codeclimate/test-reporter/formatters/clover" "github.com/codeclimate/test-reporter/formatters/cobertura" "github.com/codeclimate/test-reporter/formatters/coveragepy" + "github.com/codeclimate/test-reporter/formatters/dotcover" "github.com/codeclimate/test-reporter/formatters/excoveralls" "github.com/codeclimate/test-reporter/formatters/gcov" "github.com/codeclimate/test-reporter/formatters/gocov" @@ -19,7 +20,6 @@ import ( "github.com/codeclimate/test-reporter/formatters/lcovjson" "github.com/codeclimate/test-reporter/formatters/simplecov" "github.com/codeclimate/test-reporter/formatters/xccov" - "github.com/codeclimate/test-reporter/formatters/dotcover" "github.com/gobuffalo/envy" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/formatters/dotcover/xml.go b/formatters/dotcover/xml.go index 8911a437..79bda0c6 100644 --- a/formatters/dotcover/xml.go +++ b/formatters/dotcover/xml.go @@ -3,13 +3,13 @@ package dotcover import "encoding/xml" type xmlDotCover struct { - XMLName xml.Name `xml:"Root"` - Files []struct { - Path string `xml:"Name,attr"` - Index int `xml:"Index,attr"` + XMLName xml.Name `xml:"Root"` + Files []struct { + Path string `xml:"Name,attr"` + Index int `xml:"Index,attr"` } `xml:"FileIndices>File"` Statements []struct { - FileIndex int `xml:"FileIndex,attr"` - Covered bool `xml:"Covered,attr"` + FileIndex int `xml:"FileIndex,attr"` + Covered bool `xml:"Covered,attr"` } `xml:"Assembly>Namespace>Type>Method>Statement"` } From 93a904d39a584a83fc8be6b98757b4a0b765c61c Mon Sep 17 00:00:00 2001 From: Camillo Facello <52419977+camillof@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:48:48 -0300 Subject: [PATCH 6/6] Minor refactor to dotcover.go - avoid multiple return statements --- formatters/dotcover/dotcover.go | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/formatters/dotcover/dotcover.go b/formatters/dotcover/dotcover.go index 3e46c115..e8048dee 100644 --- a/formatters/dotcover/dotcover.go +++ b/formatters/dotcover/dotcover.go @@ -39,15 +39,9 @@ func (f Formatter) Format() (formatters.Report, error) { return rep, err } - fx, err := os.Open(f.Path) - if err != nil { - return rep, errors.WithStack(err) - } - - c := &xmlDotCover{} - err = xml.NewDecoder(fx).Decode(c) + c, err := f.readDotCoverXML() if err != nil { - return rep, errors.WithStack(err) + return rep, err } gitHead, _ := env.GetHead() @@ -55,7 +49,8 @@ func (f Formatter) Format() (formatters.Report, error) { for _, file := range c.Files { sf, err := formatters.NewSourceFile(file.Path, gitHead) if err != nil { - return rep, errors.WithStack(err) + err = errors.WithStack(err) + break } for _, statement := range c.Statements { @@ -71,9 +66,25 @@ func (f Formatter) Format() (formatters.Report, error) { err = rep.AddSourceFile(sf) if err != nil { - return rep, errors.WithStack(err) + err = errors.WithStack(err) + break } } - return rep, nil + return rep, err +} + +// readDotCoverXML reads the dotCover XML file and returns its contents. +func (f Formatter) readDotCoverXML() (*xmlDotCover, error) { + fx, err := os.Open(f.Path) + if err != nil { + return nil, errors.WithStack(err) + } + + c := &xmlDotCover{} + if err = xml.NewDecoder(fx).Decode(c); err != nil { + return nil, errors.WithStack(err) + } + + return c, nil }