From 5435ad4bc8b6e037915538d4e3eaeb6cca3e6a70 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Sat, 3 Sep 2022 17:40:32 -0400 Subject: [PATCH] Add --watch-chdir flag --- README.md | 6 ++++++ cmd/main.go | 11 ++++++++--- cmd/rerunfails.go | 2 +- cmd/rerunfails_test.go | 2 +- cmd/testdata/gotestsum-help-text | 3 ++- cmd/watch.go | 11 ++++++++--- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d61f9056..f68c019c 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/cmd/main.go b/cmd/main.go index 3c3c6c4e..4dc34964 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -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") @@ -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") @@ -163,6 +165,7 @@ type options struct { rerunFailsRunRootCases bool packages []string watch bool + watchChdir bool maxFails int version bool @@ -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 } @@ -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 diff --git a/cmd/rerunfails.go b/cmd/rerunfails.go index aadaf428..b3344c67 100644 --- a/cmd/rerunfails.go +++ b/cmd/rerunfails.go @@ -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 } diff --git a/cmd/rerunfails_test.go b/cmd/rerunfails_test.go index 0d63c082..f636094a 100644 --- a/cmd/rerunfails_test.go +++ b/cmd/rerunfails_test.go @@ -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() { diff --git a/cmd/testdata/gotestsum-help-text b/cmd/testdata/gotestsum-help-text index cc5089f0..92be18d1 100644 --- a/cmd/testdata/gotestsum-help-text +++ b/cmd/testdata/gotestsum-help-text @@ -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 diff --git a/cmd/watch.go b/cmd/watch.go index a6b6e3a1..44eeecf6 100644 --- a/cmd/watch.go +++ b/cmd/watch.go @@ -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 @@ -58,7 +63,7 @@ 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() @@ -66,7 +71,7 @@ func runSingle(opts *options) (*testjson.Execution, error) { return nil, err } - goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, rerunOpts{})) + goTestProc, err := startGoTestFn(ctx, dir, goTestCmdArgs(opts, rerunOpts{})) if err != nil { return nil, err }