From: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> Create `test-tool touch` that can update a series of files using either a pattern given on the command line or a list of files read from stdin. This will be used in a later commit to speed up p7519 which needs to generate/update many thousands of files. Signed-off-by: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> --- Makefile | 1 + t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/helper/test-touch.c | 126 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 t/helper/test-touch.c diff --git a/Makefile b/Makefile index a2a6e1f20f6..c07cfb75532 100644 --- a/Makefile +++ b/Makefile @@ -757,6 +757,7 @@ TEST_BUILTINS_OBJS += test-string-list.o TEST_BUILTINS_OBJS += test-submodule-config.o TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o +TEST_BUILTINS_OBJS += test-touch.o TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o TEST_BUILTINS_OBJS += test-userdiff.o diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index af879e4a5d7..1ad8d5fbd82 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -73,6 +73,7 @@ static struct test_cmd cmds[] = { { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, + { "touch", cmd__touch }, { "trace2", cmd__trace2 }, { "userdiff", cmd__userdiff }, { "urlmatch-normalization", cmd__urlmatch_normalization }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 6c5134b46d9..58fde0a62e5 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -63,6 +63,7 @@ int cmd__string_list(int argc, const char **argv); int cmd__submodule_config(int argc, const char **argv); int cmd__submodule_nested_repo_config(int argc, const char **argv); int cmd__subprocess(int argc, const char **argv); +int cmd__touch(int argc, const char **argv); int cmd__trace2(int argc, const char **argv); int cmd__userdiff(int argc, const char **argv); int cmd__urlmatch_normalization(int argc, const char **argv); diff --git a/t/helper/test-touch.c b/t/helper/test-touch.c new file mode 100644 index 00000000000..e9b3b754f1f --- /dev/null +++ b/t/helper/test-touch.c @@ -0,0 +1,126 @@ +/* + * test-touch.c: variation on /usr/bin/touch to speed up tests + * with a large number of files (primarily on Windows where child + * process are very, very expensive). + */ + +#include "test-tool.h" +#include "cache.h" +#include "parse-options.h" + +char *seq_pattern; +int seq_start = 1; +int seq_count = 1; + +static int do_touch_one(const char *path) +{ + int fd; + + if (!utime(path, NULL)) + return 0; + + if (errno != ENOENT) { + warning_errno("could not touch '%s'", path); + return 0; + } + + fd = open(path, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + warning_errno("could not create '%s'", path); + return 0; + } + close(fd); + + return 0; +} + +/* + * Touch a series of files. We assume that any required subdirs + * already exist. This function allows us to replace the following + * test script fragment: + * + * for i in $(test_seq 1 10000); do touch 10000_files/$i; done && + * + * with a single process: + * + * test-tool touch sequence --pattern="10000_files/%d" --start=1 --count=10000 + * + * which is much faster on Windows. + */ +static int do_sequence(void) +{ + struct strbuf buf = STRBUF_INIT; + int k; + + for (k = seq_start; k < seq_start + seq_count; k++) { + strbuf_reset(&buf); + strbuf_addf(&buf, seq_pattern, k); + + if (do_touch_one(buf.buf)) + return 1; + } + + return 0; +} + +/* + * Read a list of pathnames from stdin and touch them. We assume that + * any required subdirs already exist. + */ +static int do_stdin(void) +{ + struct strbuf buf = STRBUF_INIT; + + while (strbuf_getline(&buf, stdin) != EOF && buf.len) + if (do_touch_one(buf.buf)) + return 1; + + return 0; +} + +int cmd__touch(int argc, const char **argv) +{ + const char *touch_usage[] = { + N_("test-tool touch sequence <pattern> <start> <count>"), + N_("test-tool touch stdin"), + NULL, + }; + + struct option touch_options[] = { + OPT_GROUP(N_("sequence")), + OPT_STRING(0, "pattern", &seq_pattern, N_("format"), + N_("sequence pathname pattern")), + OPT_INTEGER(0, "start", &seq_start, + N_("sequence starting value")), + OPT_INTEGER(0, "count", &seq_count, + N_("sequence count")), + OPT_END() + }; + + const char *subcmd; + + if (argc < 2) + usage_with_options(touch_usage, touch_options); + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(touch_usage, touch_options); + + subcmd = argv[1]; + argv--; + argc++; + + argc = parse_options(argc, argv, NULL, touch_options, touch_usage, 0); + + if (!strcmp(subcmd, "sequence")) { + if (!seq_pattern || !strstr(seq_pattern, "%d")) + die("invalid sequence pattern"); + if (seq_count < 1) + die("invalid sequence count: %d", seq_count); + return !!do_sequence(); + } + + if (!strcmp(subcmd, "stdin")) { + return !!do_stdin(); + } + + die("Unhandled subcommand: '%s'", subcmd); +} -- gitgitgadget