diff options
| -rw-r--r-- | modules/dev-fkeys.el | 32 | ||||
| -rw-r--r-- | tests/test-dev-fkeys--f6-test-runner-cmd-for.el | 21 |
2 files changed, 48 insertions, 5 deletions
diff --git a/modules/dev-fkeys.el b/modules/dev-fkeys.el index 8c5b388c..836b7cf6 100644 --- a/modules/dev-fkeys.el +++ b/modules/dev-fkeys.el @@ -320,6 +320,18 @@ languages fall back to the basename without extension." ;; ---------- F6 test-runner command builder ---------- +(defconst cj/--f6-shell-safe-argument-regexp "\\`[[:alnum:]_./=+@%:,^-]+\\'" + "Regexp matching shell arguments safe to interpolate unchanged.") + +(defun cj/--f6-shell-quote-argument (argument) + "Quote ARGUMENT for shell command interpolation when needed. +Simple file paths and test regexes are returned unchanged so existing +F6 command strings stay readable. Arguments containing whitespace or +shell-significant characters are escaped with `shell-quote-argument'." + (if (string-match-p cj/--f6-shell-safe-argument-regexp argument) + argument + (shell-quote-argument argument))) + (defun cj/--f6-test-runner-cmd-for (language is-test-file rel-path stem rel-dir) "Return shell command to run tests for the given primitives, or nil. LANGUAGE is the language symbol; IS-TEST-FILE is non-nil when the file @@ -337,14 +349,24 @@ TypeScript / JavaScript and unknown languages return nil." (if is-test-file ;; The project Makefile prepends `tests/' to FILE, so pass the ;; basename only — passing the rel-path produces `tests/tests/...'. - (format "make test-file FILE=%s" (file-name-nondirectory rel-path)) - (format "make test-name TEST=^test-%s-" stem))) + (format "make test-file FILE=%s" + (cj/--f6-shell-quote-argument + (file-name-nondirectory rel-path))) + (format "make test-name TEST=%s" + (cj/--f6-shell-quote-argument + (format "^test-%s-" stem))))) ('python (if is-test-file - (format "pytest %s" rel-path) - (format "pytest tests/test_%s.py" stem))) + (format "pytest %s" (cj/--f6-shell-quote-argument rel-path)) + (format "pytest %s" + (cj/--f6-shell-quote-argument + (format "tests/test_%s.py" stem))))) ('go - (format "go test ./%s" rel-dir)) + (format "go test %s" + (cj/--f6-shell-quote-argument + (if (string-empty-p rel-dir) + "./" + (format "./%s" rel-dir))))) (_ nil))) ;; ---------- F6 current-file orchestrator ---------- diff --git a/tests/test-dev-fkeys--f6-test-runner-cmd-for.el b/tests/test-dev-fkeys--f6-test-runner-cmd-for.el index cb242889..36f97548 100644 --- a/tests/test-dev-fkeys--f6-test-runner-cmd-for.el +++ b/tests/test-dev-fkeys--f6-test-runner-cmd-for.el @@ -95,6 +95,27 @@ would over-match. Pass just the basename — the Makefile re-prepends 'go nil "main.go" "main" "") "go test ./"))) +(ert-deftest test-dev-fkeys-f6-cmd-for-python-test-file-quotes-spaces () + "Boundary: a Python test file path with spaces is shell-escaped." + (should (string= + (cj/--f6-test-runner-cmd-for + 'python t "tests/dir with spaces/test_foo.py" "foo" "tests/dir with spaces") + "pytest tests/dir\\ with\\ spaces/test_foo.py"))) + +(ert-deftest test-dev-fkeys-f6-cmd-for-elisp-source-quotes-test-regex () + "Boundary: an elisp source stem with shell metacharacters is escaped." + (should (string= + (cj/--f6-test-runner-cmd-for + 'elisp nil "modules/foo;bar.el" "foo;bar" "modules") + "make test-name TEST=\\^test-foo\\;bar-"))) + +(ert-deftest test-dev-fkeys-f6-cmd-for-go-source-quotes-spaces () + "Boundary: a Go package path with spaces is shell-escaped." + (should (string= + (cj/--f6-test-runner-cmd-for + 'go nil "pkg/with spaces/foo.go" "foo" "pkg/with spaces") + "go test ./pkg/with\\ spaces"))) + ;;; Error Cases (ert-deftest test-dev-fkeys-f6-cmd-for-typescript-returns-nil () |
