Skip to content

Commit

Permalink
Add --watch-chdir flag
Browse files Browse the repository at this point in the history
  • Loading branch information
dnephin committed Sep 3, 2022
1 parent 338c8c8 commit 5435ad4
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 9 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,12 @@ packages as command line arguments (ex: `gotestsum --watch -- ./...` or
`gotestsum --watch -- ./extrapkg`), the
tests in those packages will also be run when any file changes.

With the `--watch-chdir` flag, `gotestsum` will change the working directory
to the directory with the modified file before running tests. Changing the
directory is primarily useful when the project contains multiple Go modules.
Without this flag, `go test` will refuse to run tests for any package outside
of the main Go module.

While in watch mode, pressing some keys will perform an action:

* `r` will run tests for the previous event.
Expand Down
11 changes: 8 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ func setupFlags(name string) (*pflag.FlagSet, *options) {
"command to run after the tests have completed")
flags.BoolVar(&opts.watch, "watch", false,
"watch go files, and run tests when a file is modified")
flags.BoolVar(&opts.watchChdir, "watch-chdir", false,
"in watch mode change the working directory to the directory with the modified file before running tests")
flags.IntVar(&opts.maxFails, "max-fails", 0,
"end the test run after this number of failures")

Expand All @@ -93,7 +95,7 @@ func setupFlags(name string) (*pflag.FlagSet, *options) {
"name of the project used in the junit.xml file")

flags.IntVar(&opts.rerunFailsMaxAttempts, "rerun-fails", 0,
"rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled.")
"rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled")
flags.Lookup("rerun-fails").NoOptDefVal = "2"
flags.IntVar(&opts.rerunFailsMaxInitialFailures, "rerun-fails-max-failures", 10,
"do not rerun any tests if the initial run has more than this number of failures")
Expand Down Expand Up @@ -163,6 +165,7 @@ type options struct {
rerunFailsRunRootCases bool
packages []string
watch bool
watchChdir bool
maxFails int
version bool

Expand Down Expand Up @@ -202,7 +205,7 @@ func run(opts *options) error {
return err
}

goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, rerunOpts{}))
goTestProc, err := startGoTestFn(ctx, "", goTestCmdArgs(opts, rerunOpts{}))
if err != nil {
return err
}
Expand Down Expand Up @@ -362,13 +365,15 @@ type waiter interface {
Wait() error
}

func startGoTest(ctx context.Context, args []string) (*proc, error) {
func startGoTest(ctx context.Context, dir string, args []string) (*proc, error) {
if len(args) == 0 {
return nil, errors.New("missing command to run")
}

cmd := exec.CommandContext(ctx, args[0], args[1:]...)
cmd.Stdin = os.Stdin
cmd.Dir = dir

p := proc{cmd: cmd}
log.Debugf("exec: %s", cmd.Args)
var err error
Expand Down
2 changes: 1 addition & 1 deletion cmd/rerunfails.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func rerunFailed(ctx context.Context, opts *options, scanConfig testjson.ScanCon

nextRec := newFailureRecorder(scanConfig.Handler)
for _, tc := range tcFilter(rec.failures) {
goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, newRerunOptsFromTestCase(tc)))
goTestProc, err := startGoTestFn(ctx, "", goTestCmdArgs(opts, newRerunOptsFromTestCase(tc)))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/rerunfails_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func TestRerunFailed_ReturnsAnErrorWhenTheLastTestIsSuccessful(t *testing.T) {

func patchStartGoTestFn(f func(args []string) *proc) func() {
orig := startGoTestFn
startGoTestFn = func(ctx context.Context, args []string) (*proc, error) {
startGoTestFn = func(ctx context.Context, dir string, args []string) (*proc, error) {
return f(args), nil
}
return func() {
Expand Down
3 changes: 2 additions & 1 deletion cmd/testdata/gotestsum-help-text
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ Flags:
--packages list space separated list of package to test
--post-run-command command command to run after the tests have completed
--raw-command don't prepend 'go test -json' to the 'go test' command
--rerun-fails int[=2] rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled.
--rerun-fails int[=2] rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled
--rerun-fails-max-failures int do not rerun any tests if the initial run has more than this number of failures (default 10)
--rerun-fails-report string write a report to the file, of the tests that were rerun
--rerun-fails-run-root-test rerun the entire root testcase when any of its subtests fail, instead of only the failed subtest
--version show version and exit
--watch watch go files, and run tests when a file is modified
--watch-chdir in watch mode change the working directory to the directory with the modified file before running tests

Formats:
dots print a character for each test
Expand Down
11 changes: 8 additions & 3 deletions cmd/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,18 @@ func (w *watchRuns) run(event filewatcher.Event) error {
return nil
}

var dir string
if w.opts.watchChdir {
dir, event.PkgPath = event.PkgPath, "./"
}

opts := w.opts // shallow copy opts
opts.packages = append([]string{}, opts.packages...)
opts.packages = append(opts.packages, event.PkgPath)
opts.packages = append(opts.packages, event.Args...)

var err error
if w.prevExec, err = runSingle(&opts); !IsExitCoder(err) {
if w.prevExec, err = runSingle(&opts, dir); !IsExitCoder(err) {
return err
}
return nil
Expand All @@ -58,15 +63,15 @@ func (w *watchRuns) run(event filewatcher.Event) error {
// runSingle is similar to run. It doesn't support rerun-fails. It may be
// possible to share runSingle with run, but the defer close on the handler
// would require at least 3 return values, so for now it is a copy.
func runSingle(opts *options) (*testjson.Execution, error) {
func runSingle(opts *options, dir string) (*testjson.Execution, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

if err := opts.Validate(); err != nil {
return nil, err
}

goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, rerunOpts{}))
goTestProc, err := startGoTestFn(ctx, dir, goTestCmdArgs(opts, rerunOpts{}))
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 5435ad4

Please sign in to comment.