Skip to content

Commit

Permalink
added support for parallel execution [issue zimmski#13]
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiocicerchia committed Sep 30, 2021
1 parent 6d92170 commit 8b708fa
Showing 1 changed file with 80 additions and 35 deletions.
115 changes: 80 additions & 35 deletions cmd/go-mutesting/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
"sync"
"syscall"

"github.com/jessevdk/go-flags"
Expand Down Expand Up @@ -64,6 +66,7 @@ type options struct {
Exec string `long:"exec" description:"Execute this command for every mutation (by default the built-in exec command is used)"`
NoExec bool `long:"no-exec" description:"Skip the built-in exec command and just generate the mutations"`
Timeout uint `long:"exec-timeout" description:"Sets a timeout for the command execution (in seconds)" default:"10"`
Jobs uint `long:"jobs" description:"Allow N jobs at once" default:"1"`
} `group:"Exec options"`

Test struct {
Expand Down Expand Up @@ -158,6 +161,18 @@ func (ms *mutationStats) Total() int {
return ms.passed + ms.failed + ms.skipped
}

func getJobs(opts *options) int {
jobs := int(opts.Exec.Jobs)
if jobs < 0 {
jobs = 1
}
if jobs > runtime.NumCPU() {
jobs = runtime.NumCPU()
}

return jobs
}

func mainCmd(args []string) int {
var opts = &options{}
var mutationBlackList = map[string]struct{}{}
Expand Down Expand Up @@ -251,45 +266,33 @@ MUTATOR:

stats := &mutationStats{}

for _, file := range files {
verbose(opts, "Mutate %q", file)

src, fset, pkg, info, err := mutesting.ParseAndTypeCheckFile(file)
if err != nil {
return exitError(err.Error())
}

err = os.MkdirAll(tmpDir+"/"+filepath.Dir(file), 0755)
if err != nil {
panic(err)
}

tmpFile := tmpDir + "/" + file

originalFile := fmt.Sprintf("%s.original", tmpFile)
err = osutil.CopyFile(file, originalFile)
if err != nil {
panic(err)
}
debug(opts, "Save original into %q", originalFile)

mutationID := 0

if opts.Filter.Match != "" {
m, err := regexp.Compile(opts.Filter.Match)
if err != nil {
return exitError("Match regex is not valid: %v", err)
}
jobs := getJobs(opts)
c := make(chan string)
var wg sync.WaitGroup
wg.Add(jobs)
for runningJobs := 0; runningJobs < jobs; runningJobs++ {
go func(c chan string) {
for {
file, more := <-c
if more == false {
wg.Done()
return
}

for _, f := range astutil.Functions(src) {
if m.MatchString(f.Name.Name) {
mutationID = mutate(opts, mutators, mutationBlackList, mutationID, pkg, info, file, fset, src, f, tmpFile, execs, stats)
err := processFile(opts, tmpDir, file, mutators, mutationBlackList, execs, stats)
if err != returnOk {
// Stop execution right away.
wg.Done()
return
}
}
} else {
_ = mutate(opts, mutators, mutationBlackList, mutationID, pkg, info, file, fset, src, src, tmpFile, execs, stats)
}
}(c)
}
for _, file := range files {
c <- file
}
close(c)
wg.Wait()

if !opts.General.DoNotRemoveTmpFolder {
err = os.RemoveAll(tmpDir)
Expand All @@ -308,6 +311,48 @@ MUTATOR:
return returnOk
}

func processFile(opts *options, tmpDir, file string, mutators []mutatorItem, mutationBlackList map[string]struct{}, execs []string, stats *mutationStats) int {
verbose(opts, "Mutate %q", file)

src, fset, pkg, info, err := mutesting.ParseAndTypeCheckFile(file)
if err != nil {
return exitError(err.Error())
}

err = os.MkdirAll(tmpDir+"/"+filepath.Dir(file), 0755)
if err != nil {
panic(err)
}

tmpFile := tmpDir + "/" + file

originalFile := fmt.Sprintf("%s.original", tmpFile)
err = osutil.CopyFile(file, originalFile)
if err != nil {
panic(err)
}
debug(opts, "Save original into %q", originalFile)

mutationID := 0

if opts.Filter.Match != "" {
m, err := regexp.Compile(opts.Filter.Match)
if err != nil {
return exitError("Match regex is not valid: %v", err)
}

for _, f := range astutil.Functions(src) {
if m.MatchString(f.Name.Name) {
mutationID = mutate(opts, mutators, mutationBlackList, mutationID, pkg, info, file, fset, src, f, tmpFile, execs, stats)
}
}
} else {
_ = mutate(opts, mutators, mutationBlackList, mutationID, pkg, info, file, fset, src, src, tmpFile, execs, stats)
}

return returnOk
}

func mutate(opts *options, mutators []mutatorItem, mutationBlackList map[string]struct{}, mutationID int, pkg *types.Package, info *types.Info, file string, fset *token.FileSet, src ast.Node, node ast.Node, tmpFile string, execs []string, stats *mutationStats) int {
for _, m := range mutators {
debug(opts, "Mutator %s", m.Name)
Expand Down

0 comments on commit 8b708fa

Please sign in to comment.