From 7ecc3b3837bc57e8de9aff0beb01f44a265697ff Mon Sep 17 00:00:00 2001 From: xingyanli Date: Thu, 18 Jul 2019 14:08:21 +0800 Subject: [PATCH 1/3] modify coverage sort and add function comparing the current file with the former --- .DS_Store | Bin 0 -> 6148 bytes README.rst | 35 +++++-- cov/const.go | 6 +- cov/report.go | 256 ++++++++++++++++++++++++++++++++++++++++++-------- gocov-html.go | 11 ++- 5 files changed, 255 insertions(+), 53 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..8a425b77d15b8633073d3c39ac70792993db3e2e GIT binary patch literal 6148 zcmeH~Jr2S!425mVP>H1@V-^m;4I%_5-~tF3kvbrlb9A16778<}(6eNJu~Vz<8=6`~ zboaP!MFtV+;YQh5n3y8(<_Lbudr?Dlk!?i+r&2{{a6s|4&+&QUNOPXA0 http.html + $ gocov convert c.out | gocov-html > http.html or:: $ gocov test net/http > http.json $ gocov-html http.json > http.html +if you want to use former sorting algorithm,just set `GO_COV_HTML_REORDER` to null or do not set environment variable `GO_COV_HTML_REORDER`. + + +if you want to compare current and previous code coverage file, the former must be a json file and need be written after the filed '-diff' . In addition,you need set an environment variable `GO_COV_HTML_REORDER_TOPN` whose value is up to you. Its default value is 10. These two command will show `GO_COV_HTML_REORDER_TOPN` functions whose coverage is less than the former file:: + + $ export GO_COV_HTML_REORDER_TOPN=10 + $ gocov convert c.out | gocov-html -diff formerfilename.json> http.html + + +if you want to see functions whose coverage is less than a certain value in current code coverage file, you just need to set a environment variable `GO_COV_HTML_REORDER_NEWFUNLIMIT` whose value is up to you. this command will highlight the functions whose coverage is less than `GO_COV_HTML_REORDER_NEWFUNLIMIT`:: + + $ export GO_COV_HTML_REORDER_NEWFUNLIMIT=90 + + The generated HTML content comes along with a default embedded CSS. Use the `-s` flag to use a custom stylesheet:: diff --git a/cov/const.go b/cov/const.go index 3944efe..e03e748 100644 --- a/cov/const.go +++ b/cov/const.go @@ -21,7 +21,7 @@ package cov const ( - ProjectUrl = "https://github.com/matm/gocov-html" + ProjectUrl = "https://github.com/matm/gocov-html-reorder" htmlHeader = ` Coverage Report @@ -101,7 +101,7 @@ const ( text-align: center; font-size: 20px; font-weight: bold; - color: 375eab; + color: #375eab; } .funcname { text-align: left; @@ -149,7 +149,7 @@ const ( padding: 10px; margin: 20px; line-height: 18px; - font-size; 14px; + font-size: 14px; } a { text-decoration: none; diff --git a/cov/report.go b/cov/report.go index 2c51030..25bb1ab 100644 --- a/cov/report.go +++ b/cov/report.go @@ -26,9 +26,11 @@ import ( "github.com/axw/gocov" "io" "io/ioutil" + "log" "os" "path/filepath" "sort" + "strconv" "text/tabwriter" "time" ) @@ -49,7 +51,9 @@ type report struct { type reportFunction struct { *gocov.Function - statementsReached int + statementsReached int + diff float64 + newFunctionFlag bool } type reportFunctionList []reportFunction @@ -58,19 +62,61 @@ func (l reportFunctionList) Len() int { return len(l) } -// TODO make sort method configurable? -func (l reportFunctionList) Less(i, j int) bool { - var left, right float64 - if len(l[i].Statements) > 0 { - left = float64(l[i].statementsReached) / float64(len(l[i].Statements)) +var IsMissingLine bool +var IsCompareOldFile bool +var topN = 10 +var newFunctionLimit int +func init() { + + if os.Getenv("GO_COV_HTML_REORDER") != "" { + IsMissingLine = true } - if len(l[j].Statements) > 0 { - right = float64(l[j].statementsReached) / float64(len(l[j].Statements)) + if os.Getenv("GO_COV_HTML_REORDER_TOPN") != ""{ + str := os.Getenv("GO_COV_HTML_REORDER_TOPN") + topN = validInput(str) + } - if left < right { - return true + if os.Getenv("GO_COV_HTML_REORDER_NEWFUNLIMIT") != ""{ + str := os.Getenv("GO_COV_HTML_REORDER_NEWFUNLIMIT") + newFunctionLimit = validInput(str) + } +} + +// valid In +func validInput(str string) (int) { + value, err := strconv.Atoi(str) + if err != nil{ + log.Fatal("environment variable setting error") + } + return value +} + +func (l reportFunctionList) Less(i, j int) bool { + if IsMissingLine { + var left, right int + if len(l[i].Statements) > 0 { + left = int(l[i].statementsReached) - int(len(l[i].Statements)) + } + if len(l[j].Statements) > 0 { + right = int(l[j].statementsReached) - int(len(l[j].Statements)) + } + if left > right { + return true + } + return left == right && len(l[i].Statements) > len(l[j].Statements) + } else { + var left, right float64 + if len(l[i].Statements) > 0 { + left = float64(l[i].statementsReached) / float64(len(l[i].Statements)) + } + if len(l[j].Statements) > 0 { + right = float64(l[j].statementsReached) / float64(len(l[j].Statements)) + } + if left < right { + return true + } + return left == right && len(l[i].Statements) < len(l[j].Statements) } - return left == right && len(l[i].Statements) < len(l[j].Statements) } func (l reportFunctionList) Swap(i, j int) { @@ -111,7 +157,8 @@ func (r *report) clear() { } type reportPackageList []reportPackage - +type Foo interface { +} type reportPackage struct { pkg *gocov.Package functions reportFunctionList @@ -127,28 +174,116 @@ func (rp *reportPackage) percentageReached() float64 { return rv } -func buildReportPackage(pkg *gocov.Package) reportPackage { +// search package in old file +func boolOldPackages(r *report, p *gocov.Package) (int, error) { + i := sort.Search(len(r.packages), func(i int) bool { + return r.packages[i].Name == p.Name + }) + if i < len(r.packages) && r.packages[i].Name == p.Name { + return i, nil + } + return 0, fmt.Errorf("package Name not in old file: %q ", p.Name) +} + +// search function in package after it's package has found in old file +func addOldFunction(p *gocov.Package, function *gocov.Function) (float64,bool,error) { + for j, fn := range p.Functions { + if fn.Name == function.Name { + if len(p.Functions[j].Statements) != 0 { + reached := 0 + for _, stmt := range p.Functions[j].Statements { + if stmt.Reached > 0 { + reached++ + } + } + oldReach := float64(reached) / float64(len(p.Functions[j].Statements)) * 100 + return oldReach,false, nil + } + } + } + return 0,true, fmt.Errorf("Function Name not in old file: %q ", function.Name) +} + +type diffSort struct { + name string + diff float64 +} +// use slice,top N +func diffSortFunctions(reportPackages reportPackageList) []diffSort{ + var ds []diffSort + for _, rp := range reportPackages { + for _ ,fn := range rp.functions{ + name := rp.pkg.Name + "/"+fn.Name + df := diffSort{ + name : name, + diff : fn.diff, + } + i := sort.Search(len(ds), func(i int) bool { + return ds[i].diff <= df.diff + }) + head := ds[:i] + tail := append([]diffSort{df}, ds[i:]...) + ds = append(head,tail...) + } + } + if topN > len(ds){ + topN = len(ds) + } + ds = ds[:topN] + return ds +} + + +func buildReportPackage(pkg *gocov.Package, oldr *report) reportPackage { rv := reportPackage{ pkg: pkg, functions: make(reportFunctionList, len(pkg.Functions)), } - for i, fn := range pkg.Functions { - reached := 0 - for _, stmt := range fn.Statements { - if stmt.Reached > 0 { - reached++ + if IsCompareOldFile { + olditem, err := boolOldPackages(oldr, pkg) + var p *gocov.Package + if err == nil { + p = oldr.packages[olditem] + } + for i, fn := range pkg.Functions { + reached := 0 + for _, stmt := range fn.Statements { + if stmt.Reached > 0 { + reached++ + } + } + var oldReached float64 + var newFunctionFlag bool + if err == nil { + oldReached,newFunctionFlag,_ = addOldFunction(p, fn) + } + diff := oldReached - float64(reached) / float64(len(fn.Statements)) * 100 + if diff <= 0{ + diff = 0 } + rv.functions[i] = reportFunction{fn, reached, diff,newFunctionFlag } + rv.totalStatements += len(fn.Statements) + rv.reachedStatements += reached + } + } else { + for i, fn := range pkg.Functions { + reached := 0 + for _, stmt := range fn.Statements { + if stmt.Reached > 0 { + reached++ + } + } + rv.functions[i] = reportFunction{fn, reached, 0,true} + rv.totalStatements += len(fn.Statements) + rv.reachedStatements += reached } - rv.functions[i] = reportFunction{fn, reached} - rv.totalStatements += len(fn.Statements) - rv.reachedStatements += reached } sort.Sort(reverse{rv.functions}) return rv } // PrintReport prints a coverage report to the given writer. -func printReport(w io.Writer, r *report) { +func printReport(w io.Writer, r *report, r2 *report) { css := defaultCSS if len(r.stylesheet) > 0 { css = fmt.Sprintf("", r.stylesheet) @@ -157,7 +292,7 @@ func printReport(w io.Writer, r *report) { reportPackages := make(reportPackageList, len(r.packages)) for i, pkg := range r.packages { - reportPackages[i] = buildReportPackage(pkg) + reportPackages[i] = buildReportPackage(pkg, r2) } if len(reportPackages) == 0 { @@ -172,10 +307,11 @@ func printReport(w io.Writer, r *report) { if len(reportPackages) > 1 { summaryPackage = printReportOverview(w, reportPackages) } - w = tabwriter.NewWriter(w, 0, 8, 0, '\t', 0) + + ds := diffSortFunctions(reportPackages) for _, rp := range reportPackages { - printPackage(w, r, rp) + printPackage(w, r, rp , ds) fmt.Fprintln(w) } @@ -212,24 +348,54 @@ func printReportOverview(w io.Writer, reportPackages reportPackageList) reportPa return rv } -func printPackage(w io.Writer, r *report, rp reportPackage) { +func printPackage(w io.Writer, r *report, rp reportPackage,ds []diffSort) { fmt.Fprintf(w, "
Package Overview: %s %.2f%%
", rp.pkg.Name, rp.pkg.Name, rp.percentageReached()) fmt.Fprintf(w, overview, rp.pkg.Name, rp.pkg.Name) fmt.Fprintf(w, "\n") for _, fn := range rp.functions { + // missNumber + missingNumber := len(fn.Statements) - fn.statementsReached + reached := fn.statementsReached var stmtPercent float64 = 0 if len(fn.Statements) > 0 { stmtPercent = float64(reached) / float64(len(fn.Statements)) * 100 } - fmt.Fprintf(w, "\n", + //log.Print(fn.Name,fn.newFunctionFlag,stmtPercent) + + if newFunctionLimit!=0 && fn.newFunctionFlag && stmtPercent", fn.Name, fn.Name, fn.Name, rp.pkg.Name, filepath.Base(fn.File), stmtPercent, reached, len(fn.Statements)) + if IsMissingLine { + fmt.Fprintf(w, "", missingNumber) + } + if IsCompareOldFile { + name := rp.pkg.Name + "/"+fn.Name + if fn.diff > 0 { + for _ , df := range(ds){ + if name == df.name{ + fmt.Fprintf(w, "", fn.diff) + break + } + } + } + } + fmt.Fprintln(w, "\n") } + allMissingNumber := rp.totalStatements - rp.reachedStatements - fmt.Fprintf(w, "\n", + fmt.Fprintf(w, "", rp.pkg.Name, rp.percentageReached(), rp.reachedStatements, rp.totalStatements) + if IsMissingLine { + fmt.Fprintf(w, "", allMissingNumber) + } + fmt.Fprintln(w, "\n") fmt.Fprintf(w, "
%s(...)%s/%s%.2f%%%d/%d
%s(...)%s/%s%.2f%%%d/%d%d↓ %.2f%%
%s%.2f%%%d/%d
%s%.2f%%%d/%d%d
\n") // Embbed function source code @@ -252,10 +418,26 @@ func exists(path string) (bool, error) { // parsing JSON data generated by axw/gocov. The css parameter // is an absolute path to a custom stylesheet. Use an empty // string to use the default stylesheet available. -func HTMLReportCoverage(r io.Reader, css string) error { +func HTMLReportCoverage(r io.Reader, css string, oldFileName string,) error { report := newReport() - - // Custom stylesheet? + oldreport:= newReport() + if oldFileName != "" { + IsCompareOldFile = true + var oldR io.Reader + var errOldfile error + if oldR, errOldfile = os.Open(oldFileName); errOldfile != nil { + return fmt.Errorf("Usage: %s data.json\n", oldFileName) + } + err := oldreport.readFile(oldR) + if err != nil { + return err + } + } + err := report.readFile(r) + if err != nil { + return err + } + // Custom stylesheet stylesheet := "" if len(css) > 0 { if _, err := exists(css); err != nil { @@ -264,21 +446,21 @@ func HTMLReportCoverage(r io.Reader, css string) error { stylesheet = css } report.stylesheet = stylesheet + printReport(os.Stdout, report, oldreport) + return nil +} +func (re *report) readFile(r io.Reader) (error) { data, err := ioutil.ReadAll(r) if err != nil { - return fmt.Errorf("failed to read coverage data: %s\n", err) + return fmt.Errorf("failed to unmarshal coverage data: %s\n", err) } - packages, err := unmarshalJson(data) if err != nil { return fmt.Errorf("failed to unmarshal coverage data: %s\n", err) } - for _, pkg := range packages { - report.addPackage(pkg) + re.addPackage(pkg) } - fmt.Println() - printReport(os.Stdout, report) return nil } diff --git a/gocov-html.go b/gocov-html.go index f785f6e..8975b54 100644 --- a/gocov-html.go +++ b/gocov-html.go @@ -22,7 +22,7 @@ package main import ( "flag" - "github.com/matm/gocov-html/cov" + "github.freewheel.tv/xingyanli/gocov-html-reorder/cov" "io" "log" "os" @@ -33,21 +33,24 @@ func main() { log.SetFlags(0) var s = flag.String("s", "", "path to custom CSS file") + var oldFileName = flag.String("diff", "", "path to old json file") flag.Parse() - + //var oldFileName string switch flag.NArg() { case 0: r = os.Stdin case 1: var err error + //log.Print(flag.Arg(0)) if r, err = os.Open(flag.Arg(0)); err != nil { log.Fatal(err) } default: log.Fatalf("Usage: %s data.json\n", os.Args[0]) } - - if err := cov.HTMLReportCoverage(r, *s); err != nil { + //log.Print("oldFileName1:",*oldFileName) + if err := cov.HTMLReportCoverage(r, *s, *oldFileName); err != nil { log.Fatal(err) } } + From e69f81a498ab1141485426571815abe6a0ce8fdf Mon Sep 17 00:00:00 2001 From: XingyanLee <39521528+XingyanLee@users.noreply.github.com> Date: Thu, 18 Jul 2019 14:11:11 +0800 Subject: [PATCH 2/3] Update gocov-html.go --- gocov-html.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gocov-html.go b/gocov-html.go index 8975b54..3039598 100644 --- a/gocov-html.go +++ b/gocov-html.go @@ -22,7 +22,7 @@ package main import ( "flag" - "github.freewheel.tv/xingyanli/gocov-html-reorder/cov" + "github.com/XingyanLee/gocov-html/cov" "io" "log" "os" From b21e13a8562e6b9b0a26570ff4fadd47bdc541b1 Mon Sep 17 00:00:00 2001 From: XingyanLee <39521528+XingyanLee@users.noreply.github.com> Date: Thu, 18 Jul 2019 14:13:55 +0800 Subject: [PATCH 3/3] Update const.go --- cov/const.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cov/const.go b/cov/const.go index e03e748..bbd8234 100644 --- a/cov/const.go +++ b/cov/const.go @@ -21,7 +21,7 @@ package cov const ( - ProjectUrl = "https://github.com/matm/gocov-html-reorder" + ProjectUrl = "https://github.com/matm/gocov-html" htmlHeader = ` Coverage Report