From 0fb0487dbb96ed0a46f9807d81fd07c7dfacf2c9 Mon Sep 17 00:00:00 2001 From: jichangjun Date: Sun, 14 Jun 2020 19:37:27 +0800 Subject: [PATCH] refactor cover method --- cmd/build.go | 3 +- cmd/commonflags.go | 5 ++ cmd/cover.go | 170 +-------------------------------------------- cmd/install.go | 3 +- pkg/cover/cover.go | 164 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 174 insertions(+), 171 deletions(-) diff --git a/cmd/build.go b/cmd/build.go index 1791313..3a5e3f8 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -18,6 +18,7 @@ package cmd import ( "github.com/qiniu/goc/pkg/build" + "github.com/qiniu/goc/pkg/cover" "github.com/spf13/cobra" ) @@ -52,7 +53,7 @@ goc build -- -ldflags "-extldflags -static" -tags="embed kodo" }() // doCover with original buildFlags, with new GOPATH( tmp:original ) // in the tmp directory - doCover(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir) + cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center) // do install in the temporary directory gocBuild.Build() return diff --git a/cmd/commonflags.go b/cmd/commonflags.go index 1371929..f18f622 100644 --- a/cmd/commonflags.go +++ b/cmd/commonflags.go @@ -13,6 +13,9 @@ var ( buildFlags string packages string appArgs string + + goRunExecFlag string + goRunArguments string ) // addBasicFlags adds a @@ -41,6 +44,8 @@ func addRunFlags(cmdset *pflag.FlagSet) { addBuildFlags(cmdset) cmdset.Lookup("packages").Usage = "specify the package name, only ., ./... and *.go are supported" cmdset.StringVar(&appArgs, "appargs", "", "specify the application's arguments") + cmdset.StringVar(&goRunExecFlag, "exec", "", "same as -exec flag in 'go run' command") + cmdset.StringVar(&goRunArguments, "arguments", "", "same as 'arguments' in 'go run' command") // bind to viper viper.BindPFlags(cmdset) } diff --git a/cmd/cover.go b/cmd/cover.go index fef9874..c8eef85 100644 --- a/cmd/cover.go +++ b/cmd/cover.go @@ -17,13 +17,10 @@ package cmd import ( - "fmt" + "github.com/qiniu/goc/pkg/cover" log "github.com/sirupsen/logrus" "github.com/spf13/viper" - "os" - "strings" - "github.com/qiniu/goc/pkg/cover" "github.com/spf13/cobra" ) @@ -52,7 +49,7 @@ goc cover --center=http://127.0.0.1:7777 --target=/path/to/target --mode=atomic log.Fatalf("unknown -mode %v", mode) } - doCover(buildFlags, "", target) + cover.Execute(buildFlags, "", target, mode, center) }, } @@ -61,166 +58,3 @@ func init() { addCommonFlags(coverCmd.Flags()) rootCmd.AddCommand(coverCmd) } - -func doCover(args string, newgopath string, target string) { - if !isDirExist(target) { - log.Fatalf("target directory %s not exist", target) - } - - listArgs := []string{"-json"} - if len(args) != 0 { - listArgs = append(listArgs, args) - } - listArgs = append(listArgs, "./...") - pkgs := cover.ListPackages(target, strings.Join(listArgs, " "), newgopath) - - var seen = make(map[string]*cover.PackageCover) - var seenCache = make(map[string]*cover.PackageCover) - for _, pkg := range pkgs { - if pkg.Name == "main" { - log.Printf("handle package: %v", pkg.ImportPath) - // inject the main package - mainCover, err := cover.AddCounters(pkg, mode, newgopath) - if err != nil { - log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err) - } - - // new a testcover for this service - tc := cover.TestCover{ - Mode: mode, - Center: center, - MainPkgCover: mainCover, - } - - // handle its dependency - var internalPkgCache = make(map[string][]*cover.PackageCover) - tc.CacheCover = make(map[string]*cover.PackageCover) - for _, dep := range pkg.Deps { - - if packageCover, ok := seen[dep]; ok { - tc.DepsCover = append(tc.DepsCover, packageCover) - continue - } - - //only focus package neither standard Go library nor dependency library - if depPkg, ok := pkgs[dep]; ok { - - if findInternal(dep) { - - //scan exist cache cover to tc.CacheCover - if cache, ok := seenCache[dep]; ok { - log.Printf("cache cover exist: %s", cache.Package.ImportPath) - tc.CacheCover[cache.Package.Dir] = cache - continue - } - - // add counter for internal package - inPkgCover, err := cover.AddCounters(depPkg, mode, newgopath) - if err != nil { - log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err) - } - parentDir := getInternalParent(depPkg.Dir) - parentImportPath := getInternalParent(depPkg.ImportPath) - - //if internal parent dir or import is root path, ignore the dep. the dep is Go library nor dependency library - if parentDir == "" { - continue - } - if parentImportPath == "" { - continue - } - - pkg := &cover.Package{ - ImportPath: parentImportPath, - Dir: parentDir, - } - - // Some internal package have same parent dir or import path - // Cache all vars by internal parent dir for all child internal counter vars - cacheCover := cover.AddCacheCover(pkg, inPkgCover) - if v, ok := tc.CacheCover[cacheCover.Package.Dir]; ok { - for cVar, val := range v.Vars { - cacheCover.Vars[cVar] = val - } - tc.CacheCover[cacheCover.Package.Dir] = cacheCover - } else { - tc.CacheCover[cacheCover.Package.Dir] = cacheCover - } - - // Cache all internal vars to internal parent package - inCover := cover.CacheInternalCover(inPkgCover) - if v, ok := internalPkgCache[cacheCover.Package.Dir]; ok { - v = append(v, inCover) - internalPkgCache[cacheCover.Package.Dir] = v - } else { - var covers []*cover.PackageCover - covers = append(covers, inCover) - internalPkgCache[cacheCover.Package.Dir] = covers - } - seenCache[dep] = cacheCover - continue - } - - packageCover, err := cover.AddCounters(depPkg, mode, newgopath) - if err != nil { - log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err) - } - tc.DepsCover = append(tc.DepsCover, packageCover) - seen[dep] = packageCover - } - } - - if errs := cover.InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 { - log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs) - } - - // inject Http Cover APIs - var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir) - if err := cover.InjectCountersHandlers(tc, httpCoverApis); err != nil { - log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err) - } - } - } -} - -func isDirExist(path string) bool { - s, err := os.Stat(path) - if err != nil { - return false - } - return s.IsDir() -} - -// Refer: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L1334:6 -// findInternal looks for the final "internal" path element in the given import path. -// If there isn't one, findInternal returns ok=false. -// Otherwise, findInternal returns ok=true and the index of the "internal". -func findInternal(path string) bool { - // Three cases, depending on internal at start/end of string or not. - // The order matters: we must return the index of the final element, - // because the final one produces the most restrictive requirement - // on the importer. - switch { - case strings.HasSuffix(path, "/internal"): - return true - case strings.Contains(path, "/internal/"): - return true - case path == "internal", strings.HasPrefix(path, "internal/"): - return true - } - return false -} - -func getInternalParent(path string) string { - switch { - case strings.HasSuffix(path, "/internal"): - return strings.Split(path, "/internal")[0] - case strings.Contains(path, "/internal/"): - return strings.Split(path, "/internal/")[0] - case path == "internal": - return "" - case strings.HasPrefix(path, "internal/"): - return strings.Split(path, "internal/")[0] - } - return "" -} diff --git a/cmd/install.go b/cmd/install.go index 08659e2..625210b 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -18,6 +18,7 @@ package cmd import ( "github.com/qiniu/goc/pkg/build" + "github.com/qiniu/goc/pkg/cover" "github.com/spf13/cobra" ) @@ -49,7 +50,7 @@ goc build --buildflags="-ldflags '-extldflags -static' -tags='embed kodo'" }() // doCover with original buildFlags, with new GOPATH( tmp:original ) // in the tmp directory - doCover(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir) + cover.Execute(buildFlags, gocBuild.NewGOPATH, gocBuild.TmpDir, mode, center) // do install in the temporary directory gocBuild.Install() }, diff --git a/pkg/cover/cover.go b/pkg/cover/cover.go index 3e8db5b..b013a6d 100644 --- a/pkg/cover/cover.go +++ b/pkg/cover/cover.go @@ -22,7 +22,6 @@ import ( "crypto/sha256" "encoding/json" "fmt" - log "github.com/sirupsen/logrus" "io" "io/ioutil" "os" @@ -33,6 +32,8 @@ import ( "strings" "time" + log "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus" ) @@ -112,6 +113,125 @@ type PackageError struct { Err string // the error itself } +//Execute execute go tool cover for all the .go files in the target folder +func Execute(args, newGopath, target, mode, center string) { + if !isDirExist(target) { + log.Fatalf("target directory %s not exist", target) + } + + listArgs := []string{"-json"} + if len(args) != 0 { + listArgs = append(listArgs, args) + } + listArgs = append(listArgs, "./...") + pkgs := ListPackages(target, strings.Join(listArgs, " "), newGopath) + + var seen = make(map[string]*PackageCover) + var seenCache = make(map[string]*PackageCover) + for _, pkg := range pkgs { + if pkg.Name == "main" { + log.Printf("handle package: %v", pkg.ImportPath) + // inject the main package + mainCover, err := AddCounters(pkg, mode, newGopath) + if err != nil { + log.Fatalf("failed to add counters for pkg %s, err: %v", pkg.ImportPath, err) + } + + // new a testcover for this service + tc := TestCover{ + Mode: mode, + Center: center, + MainPkgCover: mainCover, + } + + // handle its dependency + var internalPkgCache = make(map[string][]*PackageCover) + tc.CacheCover = make(map[string]*PackageCover) + for _, dep := range pkg.Deps { + if packageCover, ok := seen[dep]; ok { + tc.DepsCover = append(tc.DepsCover, packageCover) + continue + } + + //only focus package neither standard Go library nor dependency library + if depPkg, ok := pkgs[dep]; ok { + if findInternal(dep) { + //scan exist cache cover to tc.CacheCover + if cache, ok := seenCache[dep]; ok { + log.Printf("cache cover exist: %s", cache.Package.ImportPath) + tc.CacheCover[cache.Package.Dir] = cache + continue + } + + // add counter for internal package + inPkgCover, err := AddCounters(depPkg, mode, newGopath) + if err != nil { + log.Fatalf("failed to add counters for internal pkg %s, err: %v", depPkg.ImportPath, err) + } + parentDir := getInternalParent(depPkg.Dir) + parentImportPath := getInternalParent(depPkg.ImportPath) + + //if internal parent dir or import is root path, ignore the dep. the dep is Go library nor dependency library + if parentDir == "" { + continue + } + if parentImportPath == "" { + continue + } + + pkg := &Package{ + ImportPath: parentImportPath, + Dir: parentDir, + } + + // Some internal package have same parent dir or import path + // Cache all vars by internal parent dir for all child internal counter vars + cacheCover := AddCacheCover(pkg, inPkgCover) + if v, ok := tc.CacheCover[cacheCover.Package.Dir]; ok { + for cVar, val := range v.Vars { + cacheCover.Vars[cVar] = val + } + tc.CacheCover[cacheCover.Package.Dir] = cacheCover + } else { + tc.CacheCover[cacheCover.Package.Dir] = cacheCover + } + + // Cache all internal vars to internal parent package + inCover := CacheInternalCover(inPkgCover) + if v, ok := internalPkgCache[cacheCover.Package.Dir]; ok { + v = append(v, inCover) + internalPkgCache[cacheCover.Package.Dir] = v + } else { + var covers []*PackageCover + covers = append(covers, inCover) + internalPkgCache[cacheCover.Package.Dir] = covers + } + seenCache[dep] = cacheCover + continue + } + + packageCover, err := AddCounters(depPkg, mode, newGopath) + if err != nil { + log.Fatalf("failed to add counters for pkg %s, err: %v", depPkg.ImportPath, err) + } + tc.DepsCover = append(tc.DepsCover, packageCover) + seen[dep] = packageCover + } + } + + if errs := InjectCacheCounters(internalPkgCache, tc.CacheCover); len(errs) > 0 { + log.Fatalf("failed to inject cache counters for package: %s, err: %v", pkg.ImportPath, errs) + } + + // inject Http Cover APIs + var httpCoverApis = fmt.Sprintf("%s/http_cover_apis_auto_generated.go", pkg.Dir) + if err := InjectCountersHandlers(tc, httpCoverApis); err != nil { + log.Fatalf("failed to inject counters for package: %s, err: %v", pkg.ImportPath, err) + } + } + } +} + // ListPackages list all packages under specific via go list command // The argument newgopath is if you need to go list in a different GOPATH func ListPackages(dir string, args string, newgopath string) map[string]*Package { @@ -167,6 +287,48 @@ func AddCounters(pkg *Package, mode, newgopath string) (*PackageCover, error) { }, nil } +func isDirExist(path string) bool { + s, err := os.Stat(path) + if err != nil { + return false + } + return s.IsDir() +} + +// Refer: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L1334:6 +// findInternal looks for the final "internal" path element in the given import path. +// If there isn't one, findInternal returns ok=false. +// Otherwise, findInternal returns ok=true and the index of the "internal". +func findInternal(path string) bool { + // Three cases, depending on internal at start/end of string or not. + // The order matters: we must return the index of the final element, + // because the final one produces the most restrictive requirement + // on the importer. + switch { + case strings.HasSuffix(path, "/internal"): + return true + case strings.Contains(path, "/internal/"): + return true + case path == "internal", strings.HasPrefix(path, "internal/"): + return true + } + return false +} + +func getInternalParent(path string) string { + switch { + case strings.HasSuffix(path, "/internal"): + return strings.Split(path, "/internal")[0] + case strings.Contains(path, "/internal/"): + return strings.Split(path, "/internal/")[0] + case path == "internal": + return "" + case strings.HasPrefix(path, "internal/"): + return strings.Split(path, "internal/")[0] + } + return "" +} + func buildCoverCmd(file string, coverVar *FileVar, pkg *Package, mode, newgopath string) *exec.Cmd { // to construct: go tool cover -mode=atomic -o dest src (note: dest==src) var newArgs = []string{"tool", "cover"}