diff options
author | Jacob Vosmaer (GitLab) <jacob@gitlab.com> | 2018-03-06 18:46:20 +0300 |
---|---|---|
committer | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2018-03-06 18:46:20 +0300 |
commit | d78c86c3b61b97e42b6cab098b736c0f4b983e13 (patch) | |
tree | dfb37922f33b96a7047791eee1f9ae27fee2f880 | |
parent | 50437ca090895d9cb131c6714ebf08da77041642 (diff) |
Make 'make cover' faster
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | _support/test-cover-parallel.go | 110 |
2 files changed, 111 insertions, 6 deletions
@@ -172,12 +172,7 @@ cover: prepare-tests $(GOCOVMERGE) @echo "NOTE: make cover does not exit 1 on failure, don't use it to check for tests success!" mkdir -p "$(COVERAGE_DIR)" rm -f $(COVERAGE_DIR)/*.out "$(COVERAGE_DIR)/all.merged" "$(COVERAGE_DIR)/all.html" - echo $(LOCAL_PACKAGES) > $(TARGET_DIR)/local_packages - for MOD in `cat $(TARGET_DIR)/local_packages`; do \ - go test -coverpkg=`cat $(TARGET_DIR)/local_packages |tr " " "," ` \ - -coverprofile=$(COVERAGE_DIR)/unit-`echo $$MOD|tr "/" "_"`.out \ - $$MOD 2>&1 | grep -v "no packages being tested depend on"; \ - done + go run _support/test-cover-parallel.go $(COVERAGE_DIR) $(LOCAL_PACKAGES) $(GOCOVMERGE) $(COVERAGE_DIR)/*.out > "$(COVERAGE_DIR)/all.merged" go tool cover -html "$(COVERAGE_DIR)/all.merged" -o "$(COVERAGE_DIR)/all.html" @echo "" diff --git a/_support/test-cover-parallel.go b/_support/test-cover-parallel.go new file mode 100644 index 000000000..2719f6d3c --- /dev/null +++ b/_support/test-cover-parallel.go @@ -0,0 +1,110 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "strings" + "sync" + "time" +) + +const ( + progName = "test-cover-parallel.go" +) + +func main() { + if len(os.Args) <= 2 { + log.Fatalf("usage %s OUT_DIR PKG [PKG...]", progName) + } + + outDir := os.Args[1] + packages := os.Args[2:] + + if err := buildDependentPackages(packages); err != nil { + log.Fatal(err) + } + + numWorkers := 2 + cmdChan := make(chan *exec.Cmd) + wg := &sync.WaitGroup{} + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go func() { + for cmd := range cmdChan { + runCover(cmd) + } + wg.Done() + }() + } + + packageMap := make(map[string]bool, len(packages)) + for _, pkg := range packages { + packageMap[pkg] = true + } + + for _, pkg := range packages { + deps, err := depsForPackage(pkg, packageMap) + if err != nil { + log.Fatal(err) + } + + args := []string{ + "go", + "test", + fmt.Sprintf("-coverpkg=%s", strings.Join(deps, ",")), + fmt.Sprintf("-coverprofile=%s/unit-%s.out", outDir, strings.Replace(pkg, "/", "_", -1)), + pkg, + } + + cmdChan <- exec.Command(args[0], args[1:]...) + } + close(cmdChan) + + wg.Wait() +} + +func depsForPackage(pkg string, packageMap map[string]bool) ([]string, error) { + depsOut, err := exec.Command("go", "list", "-f", `{{ join .Deps "\n" }}`, pkg).Output() + if err != nil { + return nil, err + } + + deps := []string{pkg} + for _, d := range strings.Split(string(depsOut), "\n") { + if packageMap[d] { + deps = append(deps, d) + } + } + + return deps, nil +} + +func buildDependentPackages(packages []string) error { + buildDeps := exec.Command("go", append([]string{"test", "-i"}, packages...)...) + buildDeps.Stdout = os.Stdout + buildDeps.Stderr = os.Stderr + start := time.Now() + if err := buildDeps.Run(); err != nil { + log.Printf("command failed: %s", strings.Join(buildDeps.Args, " ")) + return err + } + log.Printf("go test -i\t%.3fs", time.Since(start).Seconds()) + return nil +} + +func runCover(cmd *exec.Cmd) { + start := time.Now() + err := cmd.Run() + duration := time.Since(start) + + status := fmt.Sprintf("%s\t%.3fs", cmd.Args[len(cmd.Args)-1], duration.Seconds()) + + if err != nil { + fmt.Printf("FAIL\t%s\n", status) + fmt.Printf("command was: %s\n", strings.Join(cmd.Args, " ")) + } else { + fmt.Printf("ok \t%s\n", status) + } +} |