Repository: AlekSi/gocov-xml Branch: master Commit: 7a638c77803c Files: 9 Total size: 20.4 KB Directory structure: gitextract_7ylfuv40/ ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── coverage-04.dtd ├── coverage-with-data.xml ├── go.mod ├── go.sum └── gocov-xml.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.json coverage.xml gocov-xml ================================================ FILE: LICENSE ================================================ Copyright (c) 2013 Alexey Palazhchenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ all: fvb prepare: go get -u github.com/axw/gocov/... go get -u github.com/gorilla/mux/... gocov test -v github.com/gorilla/mux > mux.json fvb: gofmt -e -s -w . go vet . go run ./gocov-xml.go < mux.json > coverage.xml xmllint --valid --noout coverage.xml ================================================ FILE: README.md ================================================ # gocov XML A tool to generate Go coverage in XML report for using with tools/plugins like Jenkins/Cobertura. > Table of Contents - [gocov XML](#gocov-xml) - [Installation](#installation) - [Usage](#usage) - [Examples](#examples) - [Generate coverage by passing `gocov` output as input to `gocov-xml`](#generate-coverage-by-passing-gocov-output-as-input-to-gocov-xml) - [Specifying optional source](#specifying-optional-source) - [Authors](#authors) This is a simple helper tool for generating XML output in [Cobertura](http://cobertura.sourceforge.net/) format for CIs like [Jenkins](https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin), [vsts](https://www.visualstudio.com/team-services) and others from [github.com/axw/gocov](https://github.com/axw/gocov) output. The generated XML output is in the latest [coverage-04.dtd](http://cobertura.sourceforge.net/xml/coverage-04.dtd) schema ## Installation Just type the following to install the program and its dependencies: For Go 1.17 and above: ```bash go install github.com/axw/gocov/gocov@latest go install github.com/AlekSi/gocov-xml@latest ``` For previous Go versions: ```bash go get github.com/axw/gocov/... go get github.com/AlekSi/gocov-xml ``` ## Usage > **NOTE**: `gocov-xml` reads data from the standard input. ```bash gocov [-source ] ``` Where, - **`source`**: Absolute path to source. Defaults to the current working directory. ### Examples #### Generate coverage by passing `gocov` output as input to `gocov-xml` ```bash gocov test github.com/gorilla/mux | gocov-xml > coverage.xml ``` #### Specifying optional source ```bash gocov test github.com/gorilla/mux | gocov-xml -source /abs/path/to/source > coverage.xml ``` ## Authors - [Alexey Palazhchenko (AlekSi)](https://github.com/AlekSi) - [Yukinari Toyota (t-yuki)](https://github.com/t-yuki) - [Marin Bek (marinbek)](https://github.com/marinbek) - [Alex Castle (acastle)](https://github.com/acastle) - [Billy Yao (yaoyaozong)](https://github.com/yaoyaozong) - [Abhijith DA (abhijithda)](https://github.com/abhijithda) ================================================ FILE: coverage-04.dtd ================================================ ================================================ FILE: coverage-with-data.xml ================================================ C:/local/mvn-coverage-example/src/main/java --source ================================================ FILE: go.mod ================================================ module github.com/AlekSi/gocov-xml go 1.13 require github.com/axw/gocov v1.1.0 ================================================ FILE: go.sum ================================================ github.com/axw/gocov v1.1.0 h1:y5U1krExoJDlb/kNtzxyZQmNRprFOFCutWbNjcQvmVM= github.com/axw/gocov v1.1.0/go.mod h1:H9G4tivgdN3pYSSVrTFBr6kGDCmAkgbJhtxFzAvgcdw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: gocov-xml.go ================================================ package main import ( "encoding/json" "encoding/xml" "flag" "fmt" "go/token" "io/ioutil" "os" "path/filepath" "sort" "strings" "time" "github.com/axw/gocov" ) // Coverage information type Coverage struct { XMLName xml.Name `xml:"coverage"` LineRate float32 `xml:"line-rate,attr"` BranchRate float32 `xml:"branch-rate,attr"` LinesCovered float32 `xml:"lines-covered,attr"` LinesValid int64 `xml:"lines-valid,attr"` BranchesCovered int64 `xml:"branches-covered,attr"` BranchesValid int64 `xml:"branches-valid,attr"` Complexity float32 `xml:"complexity,attr"` Version string `xml:"version,attr"` Timestamp int64 `xml:"timestamp,attr"` Packages []Package `xml:"packages>package"` Sources []string `xml:"sources>source"` } // Package information type Package struct { Name string `xml:"name,attr"` LineRate float32 `xml:"line-rate,attr"` BranchRate float32 `xml:"branch-rate,attr"` Complexity float32 `xml:"complexity,attr"` Classes []Class `xml:"classes>class"` LineCount int64 `xml:"line-count,attr"` LineHits int64 `xml:"line-hits,attr"` } // Class information type Class struct { Name string `xml:"name,attr"` Filename string `xml:"filename,attr"` LineRate float32 `xml:"line-rate,attr"` BranchRate float32 `xml:"branch-rate,attr"` Complexity float32 `xml:"complexity,attr"` Methods []Method `xml:"methods>method"` Lines []Line `xml:"lines>line"` LineCount int64 `xml:"line-count,attr"` LineHits int64 `xml:"line-hits,attr"` } // Method information type Method struct { Name string `xml:"name,attr"` Signature string `xml:"signature,attr"` LineRate float32 `xml:"line-rate,attr"` BranchRate float32 `xml:"branch-rate,attr"` Complexity float32 `xml:"complexity,attr"` Lines []Line `xml:"lines>line"` LineCount int64 `xml:"line-count,attr"` LineHits int64 `xml:"line-hits,attr"` } // Line information type Line struct { Number int `xml:"number,attr"` Hits int64 `xml:"hits,attr"` } func main() { sourcePathPtr := flag.String( "source", "", "Absolute path to source. Defaults to current working directory.", ) flag.Parse() // Parse the commandline arguments. var sourcePath string var err error if *sourcePathPtr != "" { sourcePath = *sourcePathPtr if !filepath.IsAbs(sourcePath) { panic(fmt.Sprintf("Source path is a relative path: %s", sourcePath)) } } else { sourcePath, err = os.Getwd() if err != nil { panic(err) } } sources := make([]string, 1) sources[0] = sourcePath var r struct{ Packages []gocov.Package } var totalLines, totalHits int64 err = json.NewDecoder(os.Stdin).Decode(&r) if err != nil { panic(err) } fset := token.NewFileSet() tokenFiles := make(map[string]*token.File) // convert packages packages := make([]Package, len(r.Packages)) for i, gPackage := range r.Packages { // group functions by filename and "class" (type) files := make(map[string]map[string]*Class) for _, gFunction := range gPackage.Functions { // get the releative path by base path. fpath, err := filepath.Rel(sourcePath, gFunction.File) if err != nil { panic(err) } classes := files[fpath] if classes == nil { // group functions by "class" (type) in a File classes = make(map[string]*Class) files[fpath] = classes } s := strings.Split("-."+gFunction.Name, ".") // className is "-" for package-level functions className, methodName := s[len(s)-2], s[len(s)-1] class := classes[className] if class == nil { class = &Class{Name: className, Filename: fpath, Methods: []Method{}, Lines: []Line{}} classes[className] = class } // from github.com/axw/gocov /gocov/annotate.go#printFunctionSource // Load the file for line information. Probably overkill, maybe // just compute the lines from offsets in here. setContent := false tokenFile := tokenFiles[gFunction.File] if tokenFile == nil { info, err := os.Stat(gFunction.File) if err != nil { panic(err) } tokenFile = fset.AddFile(gFunction.File, fset.Base(), int(info.Size())) setContent = true } tokenData, err := ioutil.ReadFile(gFunction.File) if err != nil { panic(err) } if setContent { // This processes the content and records line number info. tokenFile.SetLinesForContent(tokenData) } // convert statements to lines lines := make([]Line, len(gFunction.Statements)) var funcHits int for i, s := range gFunction.Statements { lineno := tokenFile.Line(tokenFile.Pos(s.Start)) line := Line{Number: lineno, Hits: s.Reached} if int(s.Reached) > 0 { funcHits++ } lines[i] = line class.Lines = append(class.Lines, line) } lineRate := float32(funcHits) / float32(len(gFunction.Statements)) class.Methods = append(class.Methods, Method{Name: methodName, Lines: lines, LineRate: lineRate}) class.LineCount += int64(len(gFunction.Statements)) class.LineHits += int64(funcHits) } // fill package with "classes" p := Package{Name: gPackage.Name, Classes: []Class{}} for _, classes := range files { for _, class := range classes { p.LineCount += class.LineCount p.LineHits += class.LineHits class.LineRate = float32(class.LineHits) / float32(class.LineCount) p.Classes = append(p.Classes, *class) sort.Slice(class.Methods, func(i, j int) bool { return class.Methods[i].Name < class.Methods[j].Name }) sort.Slice(class.Lines, func(i, j int) bool { return class.Lines[i].Number < class.Lines[j].Number }) } p.LineRate = float32(p.LineHits) / float32(p.LineCount) } sort.Slice(p.Classes, func(i, j int) bool { if p.Classes[i].Filename != p.Classes[j].Filename { return p.Classes[i].Filename < p.Classes[j].Filename } return p.Classes[i].Name < p.Classes[j].Name }) packages[i] = p totalLines += p.LineCount totalHits += p.LineHits } coverage := Coverage{Sources: sources, Packages: packages, Timestamp: time.Now().UnixNano() / int64(time.Millisecond), LinesCovered: float32(totalHits), LinesValid: int64(totalLines), LineRate: float32(totalHits) / float32(totalLines)} fmt.Printf(xml.Header) fmt.Printf("\n") encoder := xml.NewEncoder(os.Stdout) encoder.Indent("", "\t") err = encoder.Encode(coverage) if err != nil { panic(err) } fmt.Println() }