Teach the testsuite runner in `test-tool run-command testsuite` how to run unit tests, by adding two new flags: First, "--(no-)run-in-shell" allows the test-tool to exec the unit-test binaries directly, rather than trying to interpret them as shell scripts. Second "--(no-)require-shell-test-pattern" bypasses the check that the test filenames match the expected t####-*.sh pattern. With these changes, you can now use test-tool to run the unit tests: $ make $ cd t/unit-tests/bin $ ../../helper/test-tool run-command testsuite --no-run-in-shell \ --no-require-shell-test-pattern This should be helpful on Windows to allow running tests without requiring Perl (for `prove`), as discussed in [1] and [2]. [1] https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109091323150.59@xxxxxxxxxxxxxxxxx/ [2] https://lore.kernel.org/git/850ea42c-f103-68d5-896b-9120e2628686@xxxxxx/ Signed-off-by: Josh Steadmon <steadmon@xxxxxxxxxx> --- t/helper/test-run-command.c | 40 +++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c index c0ed8722c8..2db7e1ef03 100644 --- a/t/helper/test-run-command.c +++ b/t/helper/test-run-command.c @@ -64,11 +64,12 @@ static int task_finished(int result UNUSED, struct testsuite { struct string_list tests, failed; int next; - int quiet, immediate, verbose, verbose_log, trace, write_junit_xml; + int quiet, immediate, verbose, verbose_log, trace, write_junit_xml, run_in_shell; }; #define TESTSUITE_INIT { \ .tests = STRING_LIST_INIT_DUP, \ .failed = STRING_LIST_INIT_DUP, \ + .run_in_shell = 1, \ } static int next_test(struct child_process *cp, struct strbuf *err, void *cb, @@ -80,7 +81,9 @@ static int next_test(struct child_process *cp, struct strbuf *err, void *cb, return 0; test = suite->tests.items[suite->next++].string; - strvec_pushl(&cp->args, "sh", test, NULL); + if (suite->run_in_shell) + strvec_push(&cp->args, "sh"); + strvec_push(&cp->args, test); if (suite->quiet) strvec_push(&cp->args, "--quiet"); if (suite->immediate) @@ -133,7 +136,7 @@ static const char * const testsuite_usage[] = { static int testsuite(int argc, const char **argv) { struct testsuite suite = TESTSUITE_INIT; - int max_jobs = 1, i, ret = 0; + int max_jobs = 1, i, ret = 0, require_shell_test_pattern = 1; DIR *dir; struct dirent *d; struct option options[] = { @@ -147,6 +150,10 @@ static int testsuite(int argc, const char **argv) OPT_BOOL('x', "trace", &suite.trace, "trace shell commands"), OPT_BOOL(0, "write-junit-xml", &suite.write_junit_xml, "write JUnit-style XML files"), + OPT_BOOL(0, "run-in-shell", &suite.run_in_shell, + "run programs in the suite via `sh`"), + OPT_BOOL(0, "require-shell-test-pattern", &require_shell_test_pattern, + "require programs to match 't####-*.sh'"), OPT_END() }; struct run_process_parallel_opts opts = { @@ -155,12 +162,21 @@ static int testsuite(int argc, const char **argv) .task_finished = test_finished, .data = &suite, }; + struct strbuf progpath = STRBUF_INIT; + size_t path_prefix_len; argc = parse_options(argc, argv, NULL, options, testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (max_jobs <= 0) max_jobs = online_cpus(); + /* + * If we run without a shell, we have to provide the relative path to + * the executables. + */ + if (!suite.run_in_shell) + strbuf_addstr(&progpath, "./"); + path_prefix_len = progpath.len; dir = opendir("."); if (!dir) @@ -168,20 +184,27 @@ static int testsuite(int argc, const char **argv) while ((d = readdir(dir))) { const char *p = d->d_name; - if (*p != 't' || !isdigit(p[1]) || !isdigit(p[2]) || - !isdigit(p[3]) || !isdigit(p[4]) || p[5] != '-' || - !ends_with(p, ".sh")) + if (!strcmp(p, ".") || !strcmp(p, "..")) continue; + if (require_shell_test_pattern) + if (*p != 't' || !isdigit(p[1]) || !isdigit(p[2]) || + !isdigit(p[3]) || !isdigit(p[4]) || p[5] != '-' || + !ends_with(p, ".sh")) + continue; /* No pattern: match all */ if (!argc) { - string_list_append(&suite.tests, p); + strbuf_setlen(&progpath, path_prefix_len); + strbuf_addstr(&progpath, p); + string_list_append(&suite.tests, progpath.buf); continue; } for (i = 0; i < argc; i++) if (!wildmatch(argv[i], p, 0)) { - string_list_append(&suite.tests, p); + strbuf_setlen(&progpath, path_prefix_len); + strbuf_addstr(&progpath, p); + string_list_append(&suite.tests, progpath.buf); break; } } @@ -208,6 +231,7 @@ static int testsuite(int argc, const char **argv) string_list_clear(&suite.tests, 0); string_list_clear(&suite.failed, 0); + strbuf_release(&progpath); return ret; } -- 2.43.0.381.gb435a96ce8-goog