Repository: gotestyourself/gotestsum
Pull request #276 (2022-09-03 21:41:53)

Add --watch-chdir flag
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.


Patch:
diff --git a/README.md b/README.md
index d61f905..f68c019 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 3c3c6c4..4dc3496 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 aadaf42..b3344c6 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/watch.go b/cmd/watch.go
index a6b6e3a..44eeecf 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
 	}


Test patch:
diff --git a/cmd/rerunfails_test.go b/cmd/rerunfails_test.go
index 0d63c08..f636094 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 cc5089f..92be18d 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