diff --git a/README.md b/README.md index 0105265..654b6e4 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ A Golang tool that does static analysis, unit testing, code review and generate code quality report. This is a tool that concurrently runs a whole bunch of those linters and normalises their output to a report: +# Branch features + - [Supported linters](#supported-linters) diff --git a/engine/config.go b/engine/config.go index 9b0fea0..2dbe50f 100644 --- a/engine/config.go +++ b/engine/config.go @@ -1,5 +1,7 @@ package engine +import "sync" + // Error contains the line number and the reason for // an error output from a command type Error struct { @@ -34,4 +36,6 @@ type Reporter struct { Metrics map[string]Metric `json:"metrics"` Issues int `json:"issues"` TimeStamp string `json:"time_stamp"` + + syncRW *sync.RWMutex } diff --git a/engine/engine.go b/engine/engine.go index f89e159..745dadb 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -35,6 +35,7 @@ func (w *WaitGroupWrapper) Wrap(cb func()) { func NewReporter(templateHtml string) *Reporter { return &Reporter{ Metrics: make(map[string]Metric, 0), + syncRW: new(sync.RWMutex), } } @@ -50,7 +51,9 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { if err != nil { glog.Errorln(err) } + r.syncRW.Lock() r.Project = PackageAbsPath(projectPath) + r.syncRW.Unlock() // linterFunction:unitTestF,Run all valid TEST in your golang package.And will measure // from both coverage and time-consuming @@ -126,9 +129,15 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { packagesTestDetail.mux.Lock() metricUnitTest.Summaries = packagesTestDetail.Values packagesTestDetail.mux.Unlock() - metricUnitTest.Percentage = sumCover / float64(countCover) + if countCover == 0 { + metricUnitTest.Percentage = 0 + } else { + metricUnitTest.Percentage = sumCover / float64(countCover) + } + r.syncRW.Lock() r.Metrics["UnitTestTips"] = metricUnitTest + r.syncRW.Unlock() glog.Infoln("unit test over!") } // All directory that has .go files will be add into. @@ -181,9 +190,10 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { metricCyclo.Summaries = summaries metricCyclo.Percentage = countPercentage(compBigThan15 + int(sumAverageCyclo/float64(len(dirsAll))) - 1) - + r.syncRW.Lock() r.Issues = r.Issues + len(summaries) r.Metrics["CycloTips"] = metricCyclo + r.syncRW.Unlock() glog.Infoln("comput cyclo done!") } // linterfunction:simpleCodeF,all golang code hints that can be optimized @@ -195,7 +205,6 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { Name: "Simple", Description: "All golang code hints that can be optimized and give suggestions for changes.", Weight: 0.1, - Summaries: make(map[string]Summary, 0), } summaries := make(map[string]Summary, 0) @@ -226,9 +235,10 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { } metricSimple.Summaries = summaries metricSimple.Percentage = countPercentage(len(summaries)) - + r.syncRW.Lock() r.Issues = r.Issues + len(summaries) r.Metrics["SimpleTips"] = metricSimple + r.syncRW.Unlock() glog.Infoln("simple code done!") } @@ -270,9 +280,10 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { } metricCopyCode.Summaries = summaries metricCopyCode.Percentage = countPercentage(len(summaries)) - + r.syncRW.Lock() r.Issues = r.Issues + len(summaries) r.Metrics["CopyCodeTips"] = metricCopyCode + r.syncRW.Unlock() glog.Infoln("checked copy code!") } // linterFunction:deadCodeF,all useless code, or never obsolete obsolete code. @@ -283,7 +294,6 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { Name: "DeadCode", Description: "All useless code, or never obsolete obsolete code.", Weight: 0.1, - Summaries: make(map[string]Summary, 0), } summaries := make(map[string]Summary, 0) @@ -313,9 +323,10 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { } metricDeadCode.Summaries = summaries metricDeadCode.Percentage = countPercentage(len(summaries)) - + r.syncRW.Lock() r.Issues = r.Issues + len(summaries) r.Metrics["DeadCodeTips"] = metricDeadCode + r.syncRW.Unlock() glog.Infoln("check dead code done.") } // linterFunction:spellCheckF,check the project variables, functions, @@ -327,7 +338,6 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { Name: "SpellCheck", Description: "Check the project variables, functions, etc. naming spelling is wrong.", Weight: 0.1, - Summaries: make(map[string]Summary, 0), } summaries := make(map[string]Summary, 0) @@ -358,9 +368,10 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { } metricSpellTips.Summaries = summaries metricSpellTips.Percentage = countPercentage(len(summaries)) - + r.syncRW.Lock() r.Issues = r.Issues + len(summaries) r.Metrics["SpellCheckTips"] = metricSpellTips + r.syncRW.Unlock() glog.Infoln("checked spell error") } // linterFunction:dependGraphF,The project contains all the package lists. @@ -379,7 +390,9 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { } metricImportPackageTips.Summaries = summaries metricImportPackageTips.Percentage = countPercentage(len(summaries)) + r.syncRW.Lock() r.Metrics["ImportPackagesTips"] = metricImportPackageTips + r.syncRW.Unlock() glog.Infoln("import packages done.") } @@ -391,7 +404,6 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { Name: "DependGraph", Description: "The dependency graph for all packages in the project helps you optimize the project architecture.", Weight: 0, - Summaries: make(map[string]Summary, 0), } summaries := make(map[string]Summary, 0) @@ -402,8 +414,10 @@ func (r *Reporter) Engine(projectPath string, exceptPackages string) { } metricDependGraphTips.Summaries = summaries metricDependGraphTips.Percentage = countPercentage(len(summaries)) + r.syncRW.Lock() r.Issues = r.Issues + len(summaries) r.Metrics["DependGraphTips"] = metricDependGraphTips + r.syncRW.Unlock() glog.Infoln("created depend graph") } r.TimeStamp = time.Now().Format("2006-01-02 15:04:05") diff --git a/linters/copycheck/copycheck.go b/linters/copycheck/copycheck.go index 74964d7..045769e 100755 --- a/linters/copycheck/copycheck.go +++ b/linters/copycheck/copycheck.go @@ -4,12 +4,12 @@ import ( "bufio" "flag" "io/ioutil" - "log" "os" "path/filepath" "sort" "strings" + "github.com/golang/glog" "github.com/wgliang/goreporter/linters/copycheck/job" "github.com/wgliang/goreporter/linters/copycheck/output" "github.com/wgliang/goreporter/linters/copycheck/syntax" @@ -38,14 +38,15 @@ const ( vendorDirInPath = string(filepath.Separator) + "vendor" + string(filepath.Separator) ) -func CopyCheck(projectPath string, expect string) [][]string { +func CopyCheck(projectPath string, expect string) (result [][]string) { flag.Parse() if html && plumbing { - log.Fatal("you can have either plumbing or HTML output") + glog.Errorln("you can have either plumbing or HTML output") + return result } paths := []string{projectPath} if verbose { - log.Println("Building suffix tree") + glog.Errorln("Building suffix tree") } schan := job.Parse(filesFeed(paths, expect)) t, data, done := job.BuildTree(schan) @@ -55,7 +56,7 @@ func CopyCheck(projectPath string, expect string) [][]string { t.Update(&syntax.Node{Type: -1}) if verbose { - log.Println("Searching for clones") + glog.Errorln("Searching for clones") } mchan := t.FindDuplOver(threshold) duplChan := make(chan syntax.Match) @@ -94,7 +95,8 @@ func crawlPaths(paths []string, expect string) chan string { for _, path := range paths { info, err := os.Lstat(path) if err != nil { - log.Fatal(err) + glog.Errorln(err) + break } if !info.IsDir() { fchan <- path diff --git a/linters/depend/depend.go b/linters/depend/depend.go index 874a92a..409c36e 100644 --- a/linters/depend/depend.go +++ b/linters/depend/depend.go @@ -5,12 +5,13 @@ import ( "fmt" "go/build" "io/ioutil" - "log" "os" "os/exec" "path/filepath" "runtime" "strings" + + "github.com/golang/glog" ) var ( @@ -51,7 +52,8 @@ func Depend(path, expect string) string { args := []string{path} if len(args) != 1 { - log.Fatal("need one package name to process") + glog.Errorln("need one package name to process") + return "" } if ignorePrefixes != "" { @@ -72,10 +74,12 @@ func Depend(path, expect string) string { cwd, err := os.Getwd() if err != nil { - log.Fatalf("failed to get cwd: %s", err) + glog.Errorf("failed to get cwd: %s", err) + return "" } if err := processPackage(cwd, args[0]); err != nil { - log.Fatal(err) + glog.Errorln(err) + return "" } graph := "digraph godep {" @@ -122,7 +126,7 @@ func Depend(path, expect string) string { err = ioutil.WriteFile("graph.gv", []byte(graph), 0666) if err != nil { - log.Println(err) + glog.Errorln(err) } // convert file formate @@ -132,22 +136,22 @@ func Depend(path, expect string) string { cmdsvg.Stderr = os.Stderr err = cmdsvg.Run() if err != nil { - log.Println(err) + glog.Errorln(err) } svg, err := ioutil.ReadFile("pkgdep.svg") if err != nil { - log.Println(err) + glog.Errorln(err) } err = os.Remove("pkgdep.svg") if err != nil { - log.Println(err) + glog.Errorln(err) } err = os.Remove("graph.gv") if err != nil { - log.Println(err) + glog.Errorln(err) } return string(svg) @@ -270,7 +274,7 @@ func getVendorlist(path string) []string { return nil }) if err != nil { - log.Printf("filepath.Walk() returned %v\n", err) + glog.Errorf("filepath.Walk() returned %v\n", err) } return vendors } @@ -278,11 +282,12 @@ func getVendorlist(path string) []string { func PackageAbsPath(path string) (packagePath string) { _, err := os.Stat(path) if err != nil { - log.Fatal("package path is invalid") + glog.Errorln("package path is invalid") + return "" } absPath, err := filepath.Abs(path) if err != nil { - log.Println(err) + glog.Errorln(err) } packagePathIndex := strings.Index(absPath, "src") if -1 != packagePathIndex { diff --git a/tools/report2html.go b/tools/report2html.go index 99735e3..b4f2408 100644 --- a/tools/report2html.go +++ b/tools/report2html.go @@ -200,10 +200,12 @@ func Json2Html(jsonData []byte) (HtmlData, error) { htmlData.Issues = issues htmlData.Date = structData.TimeStamp - if len(importPackages) > 0 { + if len(importPackages) > 0 && len(noTestPackages) == 0 { + htmlData.AveragePackageCover = float64(100) + } else if len(importPackages) > 0 { htmlData.AveragePackageCover = float64(100 * (len(importPackages) - len(noTestPackages)) / len(importPackages)) } else { - htmlData.AveragePackageCover = float64(100) + htmlData.AveragePackageCover = float64(0) } return htmlData, nil }