Skip to content

Commit

Permalink
Clean up run command
Browse files Browse the repository at this point in the history
Split up argument parsing into output-related
and execution-related options. Also break up
argument processing from actual command running
so we can reuse these primitives for the upcoming
`qa auto` functionality.

Also switch to -squash argument instead of suffixes
for runner types.

Use io.Closer interface in a number of places rather
than defer foo.Close(). Also start properly cleaning
up signal handlers and other cruft that the run
command generates.

Avoid double-closing server.Server instances, so we
can reuse them.

This is the first step towards automatically running
tests on file save.
  • Loading branch information
ajbouh committed Jul 24, 2016
1 parent 2a12f24 commit 978f98f
Show file tree
Hide file tree
Showing 14 changed files with 693 additions and 456 deletions.
140 changes: 140 additions & 0 deletions src/qa/cmd/run/execution-flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package run

import (
"errors"
"flag"
"fmt"
"math/rand"
"runtime"
"strings"

"qa/cmd"
"qa/emitter"
"qa/runner"
"qa/runner/server"
)

type executionFlags struct {
jobs *int
squashPolicy *runner.SquashPolicy
listenNetwork *string
listenAddress *string
errorsCaptureLocals *string
captureStandardFds *bool
evalBeforeFork *string
evalAfterFork *string
sampleStack *bool
warmup *bool
seed *int
}

type squashPolicyValue struct {
value *runner.SquashPolicy
}

func (v *squashPolicyValue) String() string {
switch *v.value {
case runner.SquashAll:
return "all"
case runner.SquashNothing:
return "nothing"
case runner.SquashByFile:
return "file"
}

return ""
}

func (v *squashPolicyValue) Set(s string) error {
switch s {
case "all":
*v.value = runner.SquashAll
case "none":
*v.value = runner.SquashNothing
case "file":
*v.value = runner.SquashByFile
default:
return errors.New("Invalid squash policy: " + s)
}

return nil
}

func defineExecutionFlags(flags *flag.FlagSet) *executionFlags {
squashPolicyValue := &squashPolicyValue{new(runner.SquashPolicy)}
*squashPolicyValue.value = runner.SquashByFile
flags.Var(squashPolicyValue, "squash", "One of: all, none, file")

return &executionFlags{
seed: flags.Int("seed", int(rand.Int31()), "Set seed to use"),
jobs: flags.Int("jobs", runtime.NumCPU(), "Set number of jobs"),
squashPolicy: squashPolicyValue.value,
listenNetwork: flags.String("listen-network", "unix", "Specify unix or tcp socket for worker coordination"),
listenAddress: flags.String("listen-address", "/tmp/qa", "Listen address for worker coordination"),
errorsCaptureLocals: flags.String("errors-capture-locals", "false", "Use runtime debug API to capture locals from stack when raising errors"),
captureStandardFds: flags.Bool("capture-standard-fds", true, "Capture stdout and stderr"),
evalBeforeFork: flags.String("eval-before-fork", "", "Execute the given code before forking any workers or loading any files"),
evalAfterFork: flags.String("eval-after-fork", "", "Execute the given code after a work forks, but before work begins"),
sampleStack: flags.Bool("sample-stack", false, "Enable stack sampling"),
warmup: flags.Bool("warmup", false, "Use a variety of experimental heuristics to warm up worker caches"),
}
}

func (f *executionFlags) Listen() (*server.Server, error) {
return server.Listen(*f.listenNetwork, *f.listenAddress)
}

func (f *executionFlags) WorkerEnvs() []map[string]string {
workerEnvs := []map[string]string{}
for i := 0; i < *f.jobs; i++ {
workerEnvs = append(workerEnvs,
map[string]string{"QA_WORKER": fmt.Sprintf("%d", i)})
}

return workerEnvs
}

func (f *executionFlags) RunnerConfigs(env *cmd.Env, runnerSpecs []string) []runner.Config {
var configs []runner.Config
for _, runnerSpec := range runnerSpecs {
runnerSpecSplit := strings.Split(runnerSpec, ":")
runnerName := runnerSpecSplit[0]
var lister runner.FileLister
if len(runnerSpecSplit) == 1 {
lister = runner.NewFileGlob(env.Dir, []string{emitter.DefaultGlob(runnerName)})
} else {
lister = runner.NewFileGlob(env.Dir, runnerSpecSplit[1:])
}

configs = append(configs, runner.Config{
Name: runnerName,
FileLister: lister,
Seed: *f.seed,
Dir: env.Dir,
EnvVars: env.Vars,
SquashPolicy: *f.squashPolicy,
// Enable entries below to add specific method calls (and optionally their arguments) to the trace.
TraceProbes: []string{
// "Kernel#require(path)",
// "Kernel#load",
// "ActiveRecord::ConnectionAdapters::Mysql2Adapter#execute(sql,name)",
// "ActiveRecord::ConnectionAdapters::PostgresSQLAdapter#execute_and_clear(sql,name,binds)",
// "ActiveSupport::Dependencies::Loadable#require(path)",
// "ActiveRecord::ConnectionAdapters::QueryCache#clear_query_cache",
// "ActiveRecord::ConnectionAdapters::SchemaCache#initialize",
// "ActiveRecord::ConnectionAdapters::SchemaCache#clear!",
// "ActiveRecord::ConnectionAdapters::SchemaCache#clear_table_cache!",
},
PassthroughConfig: map[string](interface{}){
"warmup": *f.warmup,
"errorsCaptureLocals": *f.errorsCaptureLocals,
"captureStandardFds": *f.captureStandardFds,
"evalBeforeFork": *f.evalBeforeFork,
"evalAfterFork": *f.evalAfterFork,
"sampleStack": *f.sampleStack,
},
})
}

return configs
}
Loading

0 comments on commit 978f98f

Please sign in to comment.