diff options
Diffstat (limited to 't/helper')
57 files changed, 2844 insertions, 371 deletions
diff --git a/t/helper/.gitignore b/t/helper/.gitignore index 48c7bb0..8c2ddcc 100644 --- a/t/helper/.gitignore +++ b/t/helper/.gitignore @@ -1,4 +1,2 @@ /test-tool /test-fake-ssh -/test-line-buffer -/test-svn-fe diff --git a/t/helper/test-advise.c b/t/helper/test-advise.c new file mode 100644 index 0000000..cb88113 --- /dev/null +++ b/t/helper/test-advise.c @@ -0,0 +1,22 @@ +#include "test-tool.h" +#include "cache.h" +#include "advice.h" +#include "config.h" + +int cmd__advise_if_enabled(int argc, const char **argv) +{ + if (argc != 2) + die("usage: %s <advice>", argv[0]); + + setup_git_directory(); + git_config(git_default_config, NULL); + + /* + * Any advice type can be used for testing, but NESTED_TAG was + * selected here and in t0018 where this command is being + * executed. + */ + advise_if_enabled(ADVICE_NESTED_TAG, "%s", argv[1]); + + return 0; +} diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c new file mode 100644 index 0000000..ff35f59 --- /dev/null +++ b/t/helper/test-bitmap.c @@ -0,0 +1,32 @@ +#include "test-tool.h" +#include "cache.h" +#include "pack-bitmap.h" + +static int bitmap_list_commits(void) +{ + return test_bitmap_commits(the_repository); +} + +static int bitmap_dump_hashes(void) +{ + return test_bitmap_hashes(the_repository); +} + +int cmd__bitmap(int argc, const char **argv) +{ + setup_git_directory(); + + if (argc != 2) + goto usage; + + if (!strcmp(argv[1], "list-commits")) + return bitmap_list_commits(); + if (!strcmp(argv[1], "dump-hashes")) + return bitmap_dump_hashes(); + +usage: + usage("\ttest-tool bitmap list-commits\n" + "\ttest-tool bitmap dump-hashes"); + + return -1; +} diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c new file mode 100644 index 0000000..ad3ef1c --- /dev/null +++ b/t/helper/test-bloom.c @@ -0,0 +1,97 @@ +#include "git-compat-util.h" +#include "bloom.h" +#include "test-tool.h" +#include "commit.h" + +static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS; + +static void add_string_to_filter(const char *data, struct bloom_filter *filter) { + struct bloom_key key; + int i; + + fill_bloom_key(data, strlen(data), &key, &settings); + printf("Hashes:"); + for (i = 0; i < settings.num_hashes; i++){ + printf("0x%08x|", key.hashes[i]); + } + printf("\n"); + add_key_to_filter(&key, filter, &settings); +} + +static void print_bloom_filter(struct bloom_filter *filter) { + int i; + + if (!filter) { + printf("No filter.\n"); + return; + } + printf("Filter_Length:%d\n", (int)filter->len); + printf("Filter_Data:"); + for (i = 0; i < filter->len; i++) { + printf("%02x|", filter->data[i]); + } + printf("\n"); +} + +static void get_bloom_filter_for_commit(const struct object_id *commit_oid) +{ + struct commit *c; + struct bloom_filter *filter; + setup_git_directory(); + c = lookup_commit(the_repository, commit_oid); + filter = get_or_compute_bloom_filter(the_repository, c, 1, + &settings, + NULL); + print_bloom_filter(filter); +} + +static const char *bloom_usage = "\n" +" test-tool bloom get_murmur3 <string>\n" +" test-tool bloom generate_filter <string> [<string>...]\n" +" test-tool bloom get_filter_for_commit <commit-hex>\n"; + +int cmd__bloom(int argc, const char **argv) +{ + setup_git_directory(); + + if (argc < 2) + usage(bloom_usage); + + if (!strcmp(argv[1], "get_murmur3")) { + uint32_t hashed; + if (argc < 3) + usage(bloom_usage); + hashed = murmur3_seeded(0, argv[2], strlen(argv[2])); + printf("Murmur3 Hash with seed=0:0x%08x\n", hashed); + } + + if (!strcmp(argv[1], "generate_filter")) { + struct bloom_filter filter; + int i = 2; + filter.len = (settings.bits_per_entry + BITS_PER_WORD - 1) / BITS_PER_WORD; + CALLOC_ARRAY(filter.data, filter.len); + + if (argc - 1 < i) + usage(bloom_usage); + + while (argv[i]) { + add_string_to_filter(argv[i], &filter); + i++; + } + + print_bloom_filter(&filter); + } + + if (!strcmp(argv[1], "get_filter_for_commit")) { + struct object_id oid; + const char *end; + if (argc < 3) + usage(bloom_usage); + if (parse_oid_hex(argv[2], &oid, &end)) + die("cannot parse oid '%s'", argv[2]); + init_bloom_filters(); + get_bloom_filter_for_commit(&oid); + } + + return 0; +} diff --git a/t/helper/test-chmtime.c b/t/helper/test-chmtime.c index aa22af4..dc28890 100644 --- a/t/helper/test-chmtime.c +++ b/t/helper/test-chmtime.c @@ -109,9 +109,9 @@ int cmd__chmtime(int argc, const char **argv) uintmax_t mtime; if (stat(argv[i], &sb) < 0) { - fprintf(stderr, "Failed to stat %s: %s\n", + fprintf(stderr, "Failed to stat %s: %s. Skipping\n", argv[i], strerror(errno)); - return 1; + continue; } #ifdef GIT_WINDOWS_NATIVE @@ -134,6 +134,21 @@ int cmd__chmtime(int argc, const char **argv) } if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) { +#ifdef GIT_WINDOWS_NATIVE + if (S_ISDIR(sb.st_mode)) { + /* + * NEEDSWORK: The Windows version of `utime()` + * (aka `mingw_utime()`) does not correctly + * handle directory arguments, since it uses + * `_wopen()`. Ignore it for now since this + * is just a test. + */ + fprintf(stderr, + ("Failed to modify time on directory %s. " + "Skipping\n"), argv[i]); + continue; + } +#endif fprintf(stderr, "Failed to modify time on %s: %s\n", argv[i], strerror(errno)); return 1; diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 214003d..a6e9367 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -37,21 +37,6 @@ * */ -static const char *scope_name(enum config_scope scope) -{ - switch (scope) { - case CONFIG_SCOPE_SYSTEM: - return "system"; - case CONFIG_SCOPE_GLOBAL: - return "global"; - case CONFIG_SCOPE_REPO: - return "repo"; - case CONFIG_SCOPE_CMDLINE: - return "cmdline"; - default: - return "unknown"; - } -} static int iterate_cb(const char *var, const char *value, void *data) { static int nr; @@ -63,7 +48,8 @@ static int iterate_cb(const char *var, const char *value, void *data) printf("value=%s\n", value ? value : "(null)"); printf("origin=%s\n", current_config_origin_type()); printf("name=%s\n", current_config_name()); - printf("scope=%s\n", scope_name(current_config_scope())); + printf("lno=%d\n", current_config_line()); + printf("scope=%s\n", config_scope_name(current_config_scope())); return 0; } @@ -140,7 +126,7 @@ int cmd__config(int argc, const char **argv) goto exit1; } } else if (argc == 3 && !strcmp(argv[1], "get_string")) { - if (!git_config_get_string_const(argv[2], &v)) { + if (!git_config_get_string_tmp(argv[2], &v)) { printf("%s\n", v); goto exit0; } else { diff --git a/t/helper/test-crontab.c b/t/helper/test-crontab.c new file mode 100644 index 0000000..e7c0137 --- /dev/null +++ b/t/helper/test-crontab.c @@ -0,0 +1,35 @@ +#include "test-tool.h" +#include "cache.h" + +/* + * Usage: test-tool cron <file> [-l] + * + * If -l is specified, then write the contents of <file> to stdout. + * Otherwise, write from stdin into <file>. + */ +int cmd__crontab(int argc, const char **argv) +{ + int a; + FILE *from, *to; + + if (argc == 3 && !strcmp(argv[2], "-l")) { + from = fopen(argv[1], "r"); + if (!from) + return 0; + to = stdout; + } else if (argc == 2) { + from = stdin; + to = fopen(argv[1], "w"); + } else + return error("unknown arguments"); + + while ((a = fgetc(from)) != EOF) + fputc(a, to); + + if (argc == 3) + fclose(from); + else + fclose(to); + + return 0; +} diff --git a/t/helper/test-csprng.c b/t/helper/test-csprng.c new file mode 100644 index 0000000..65d1497 --- /dev/null +++ b/t/helper/test-csprng.c @@ -0,0 +1,29 @@ +#include "test-tool.h" +#include "git-compat-util.h" + + +int cmd__csprng(int argc, const char **argv) +{ + unsigned long count; + unsigned char buf[1024]; + + if (argc > 2) { + fprintf(stderr, "usage: %s [<size>]\n", argv[0]); + return 2; + } + + count = (argc == 2) ? strtoul(argv[1], NULL, 0) : -1L; + + while (count) { + unsigned long chunk = count < sizeof(buf) ? count : sizeof(buf); + if (csprng_bytes(buf, chunk) < 0) { + perror("failed to read"); + return 5; + } + if (fwrite(buf, chunk, 1, stdout) != chunk) + return 1; + count -= chunk; + } + + return 0; +} diff --git a/t/helper/test-date.c b/t/helper/test-date.c index 099eff4..45951b1 100644 --- a/t/helper/test-date.c +++ b/t/helper/test-date.c @@ -1,5 +1,6 @@ #include "test-tool.h" #include "cache.h" +#include "date.h" static const char *usage_msg = "\n" " test-tool date relative [time_t]...\n" @@ -34,7 +35,7 @@ static void show_human_dates(const char **argv) static void show_dates(const char **argv, const char *format) { - struct date_mode mode; + struct date_mode mode = DATE_MODE_INIT; parse_date_format(format, &mode); for (; *argv; argv++) { @@ -53,6 +54,8 @@ static void show_dates(const char **argv, const char *format) printf("%s -> %s\n", *argv, show_date(t, tz, &mode)); } + + date_mode_release(&mode); } static void parse_dates(const char **argv) diff --git a/t/helper/test-drop-caches.c b/t/helper/test-drop-caches.c index 7b42784..e37396d 100644 --- a/t/helper/test-drop-caches.c +++ b/t/helper/test-drop-caches.c @@ -3,6 +3,7 @@ #if defined(GIT_WINDOWS_NATIVE) #include "lazyload.h" +#include <winnt.h> static int cmd_sync(void) { @@ -86,7 +87,8 @@ static int cmd_dropcaches(void) { HANDLE hProcess = GetCurrentProcess(); HANDLE hToken; - DECLARE_PROC_ADDR(ntdll.dll, DWORD, NtSetSystemInformation, INT, PVOID, ULONG); + DECLARE_PROC_ADDR(ntdll.dll, DWORD, NTAPI, NtSetSystemInformation, INT, PVOID, + ULONG); SYSTEM_MEMORY_LIST_COMMAND command; int status; diff --git a/t/helper/test-dump-fsmonitor.c b/t/helper/test-dump-fsmonitor.c index 2786f47..975f0ac 100644 --- a/t/helper/test-dump-fsmonitor.c +++ b/t/helper/test-dump-fsmonitor.c @@ -13,7 +13,7 @@ int cmd__dump_fsmonitor(int ac, const char **av) printf("no fsmonitor\n"); return 0; } - printf("fsmonitor last update %"PRIuMAX"\n", (uintmax_t)istate->fsmonitor_last_update); + printf("fsmonitor last update %s\n", istate->fsmonitor_last_update); for (i = 0; i < istate->cache_nr; i++) printf((istate->cache[i]->ce_flags & CE_FSMONITOR_VALID) ? "+" : "-"); diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c index 63c689d..a209880 100644 --- a/t/helper/test-dump-split-index.c +++ b/t/helper/test-dump-split-index.c @@ -13,6 +13,8 @@ int cmd__dump_split_index(int ac, const char **av) struct split_index *si; int i; + setup_git_directory(); + do_read_index(&the_index, av[1], 1); printf("own %s\n", oid_to_hex(&the_index.oid)); si = the_index.split_index; diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c index cf0f2c7..9901061 100644 --- a/t/helper/test-dump-untracked-cache.c +++ b/t/helper/test-dump-untracked-cache.c @@ -45,8 +45,10 @@ int cmd__dump_untracked_cache(int ac, const char **av) struct untracked_cache *uc; struct strbuf base = STRBUF_INIT; - /* Hack to avoid modifying the untracked cache when we read it */ - ignore_untracked_cache_config = 1; + /* Set core.untrackedCache=keep before setup_git_directory() */ + xsetenv("GIT_CONFIG_COUNT", "1", 1); + xsetenv("GIT_CONFIG_KEY_0", "core.untrackedCache", 1); + xsetenv("GIT_CONFIG_VALUE_0", "keep", 1); setup_git_directory(); if (read_cache() < 0) diff --git a/t/helper/test-example-decorate.c b/t/helper/test-example-decorate.c index c8a1cde..b9d1200 100644 --- a/t/helper/test-example-decorate.c +++ b/t/helper/test-example-decorate.c @@ -26,8 +26,8 @@ int cmd__example_decorate(int argc, const char **argv) * Add 2 objects, one with a non-NULL decoration and one with a NULL * decoration. */ - one = lookup_unknown_object(&one_oid); - two = lookup_unknown_object(&two_oid); + one = lookup_unknown_object(the_repository, &one_oid); + two = lookup_unknown_object(the_repository, &two_oid); ret = add_decoration(&n, one, &decoration_a); if (ret) BUG("when adding a brand-new object, NULL should be returned"); @@ -56,7 +56,7 @@ int cmd__example_decorate(int argc, const char **argv) ret = lookup_decoration(&n, two); if (ret != &decoration_b) BUG("lookup should return added declaration"); - three = lookup_unknown_object(&three_oid); + three = lookup_unknown_object(the_repository, &three_oid); ret = lookup_decoration(&n, three); if (ret) BUG("lookup for unknown object should return NULL"); diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c new file mode 100644 index 0000000..4e5553e --- /dev/null +++ b/t/helper/test-fast-rebase.c @@ -0,0 +1,234 @@ +/* + * "git fast-rebase" builtin command + * + * FAST: Forking Any Subprocesses (is) Taboo + * + * This is meant SOLELY as a demo of what is possible. sequencer.c and + * rebase.c should be refactored to use the ideas here, rather than attempting + * to extend this file to replace those (unless Phillip or Dscho say that + * refactoring is too hard and we need a clean slate, but I'm guessing that + * refactoring is the better route). + */ + +#define USE_THE_INDEX_COMPATIBILITY_MACROS +#include "test-tool.h" + +#include "cache-tree.h" +#include "commit.h" +#include "lockfile.h" +#include "merge-ort.h" +#include "refs.h" +#include "revision.h" +#include "sequencer.h" +#include "strvec.h" +#include "tree.h" + +static const char *short_commit_name(struct commit *commit) +{ + return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV); +} + +static struct commit *peel_committish(const char *name) +{ + struct object *obj; + struct object_id oid; + + if (get_oid(name, &oid)) + return NULL; + obj = parse_object(the_repository, &oid); + return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); +} + +static char *get_author(const char *message) +{ + size_t len; + const char *a; + + a = find_commit_header(message, "author", &len); + if (a) + return xmemdupz(a, len); + + return NULL; +} + +static struct commit *create_commit(struct tree *tree, + struct commit *based_on, + struct commit *parent) +{ + struct object_id ret; + struct object *obj; + struct commit_list *parents = NULL; + char *author; + char *sign_commit = NULL; + struct commit_extra_header *extra; + struct strbuf msg = STRBUF_INIT; + const char *out_enc = get_commit_output_encoding(); + const char *message = logmsg_reencode(based_on, NULL, out_enc); + const char *orig_message = NULL; + const char *exclude_gpgsig[] = { "gpgsig", NULL }; + + commit_list_insert(parent, &parents); + extra = read_commit_extra_headers(based_on, exclude_gpgsig); + find_commit_subject(message, &orig_message); + strbuf_addstr(&msg, orig_message); + author = get_author(message); + reset_ident_date(); + if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents, + &ret, author, NULL, sign_commit, extra)) { + error(_("failed to write commit object")); + return NULL; + } + free(author); + strbuf_release(&msg); + + obj = parse_object(the_repository, &ret); + return (struct commit *)obj; +} + +int cmd__fast_rebase(int argc, const char **argv) +{ + struct commit *onto; + struct commit *last_commit = NULL, *last_picked_commit = NULL; + struct object_id head; + struct lock_file lock = LOCK_INIT; + struct strvec rev_walk_args = STRVEC_INIT; + struct rev_info revs; + struct commit *commit; + struct merge_options merge_opt; + struct tree *next_tree, *base_tree, *head_tree; + struct merge_result result; + struct strbuf reflog_msg = STRBUF_INIT; + struct strbuf branch_name = STRBUF_INIT; + int ret = 0; + + /* + * test-tool stuff doesn't set up the git directory by default; need to + * do that manually. + */ + setup_git_directory(); + + if (argc == 2 && !strcmp(argv[1], "-h")) { + printf("Sorry, I am not a psychiatrist; I can not give you the help you need. Oh, you meant usage...\n"); + exit(129); + } + + if (argc != 5 || strcmp(argv[1], "--onto")) + die("usage: read the code, figure out how to use it, then do so"); + + onto = peel_committish(argv[2]); + strbuf_addf(&branch_name, "refs/heads/%s", argv[4]); + + /* Sanity check */ + if (get_oid("HEAD", &head)) + die(_("Cannot read HEAD")); + assert(oideq(&onto->object.oid, &head)); + + hold_locked_index(&lock, LOCK_DIE_ON_ERROR); + if (repo_read_index(the_repository) < 0) + BUG("Could not read index"); + + repo_init_revisions(the_repository, &revs, NULL); + revs.verbose_header = 1; + revs.max_parents = 1; + revs.cherry_mark = 1; + revs.limited = 1; + revs.reverse = 1; + revs.right_only = 1; + revs.sort_order = REV_SORT_IN_GRAPH_ORDER; + revs.topo_order = 1; + strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL); + + if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) { + ret = error(_("unhandled options")); + goto cleanup; + } + + strvec_clear(&rev_walk_args); + + if (prepare_revision_walk(&revs) < 0) { + ret = error(_("error preparing revisions")); + goto cleanup; + } + + init_merge_options(&merge_opt, the_repository); + memset(&result, 0, sizeof(result)); + merge_opt.show_rename_progress = 1; + merge_opt.branch1 = "HEAD"; + head_tree = get_commit_tree(onto); + result.tree = head_tree; + last_commit = onto; + while ((commit = get_revision(&revs))) { + struct commit *base; + + fprintf(stderr, "Rebasing %s...\r", + oid_to_hex(&commit->object.oid)); + assert(commit->parents && !commit->parents->next); + base = commit->parents->item; + + next_tree = get_commit_tree(commit); + base_tree = get_commit_tree(base); + + merge_opt.branch2 = short_commit_name(commit); + merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2); + + merge_incore_nonrecursive(&merge_opt, + base_tree, + result.tree, + next_tree, + &result); + + free((char*)merge_opt.ancestor); + merge_opt.ancestor = NULL; + if (!result.clean) + break; + last_picked_commit = commit; + last_commit = create_commit(result.tree, commit, last_commit); + } + /* TODO: There should be some kind of rev_info_free(&revs) call... */ + memset(&revs, 0, sizeof(revs)); + + merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean); + + if (result.clean < 0) + exit(128); + + if (result.clean) { + fprintf(stderr, "\nDone.\n"); + strbuf_addf(&reflog_msg, "finish rebase %s onto %s", + oid_to_hex(&last_picked_commit->object.oid), + oid_to_hex(&last_commit->object.oid)); + if (update_ref(reflog_msg.buf, branch_name.buf, + &last_commit->object.oid, + &last_picked_commit->object.oid, + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) { + error(_("could not update %s"), argv[4]); + die("Failed to update %s", argv[4]); + } + if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0) + die(_("unable to update HEAD")); + + prime_cache_tree(the_repository, the_repository->index, + result.tree); + } else { + fprintf(stderr, "\nAborting: Hit a conflict.\n"); + strbuf_addf(&reflog_msg, "rebase progress up to %s", + oid_to_hex(&last_picked_commit->object.oid)); + if (update_ref(reflog_msg.buf, "HEAD", + &last_commit->object.oid, + &head, + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) { + error(_("could not update %s"), argv[4]); + die("Failed to update %s", argv[4]); + } + } + if (write_locked_index(&the_index, &lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) + die(_("unable to write %s"), get_index_file()); + + ret = (result.clean == 0); +cleanup: + strbuf_release(&reflog_msg); + strbuf_release(&branch_name); + release_revisions(&revs); + return ret; +} diff --git a/t/helper/test-fsmonitor-client.c b/t/helper/test-fsmonitor-client.c new file mode 100644 index 0000000..54a4856 --- /dev/null +++ b/t/helper/test-fsmonitor-client.c @@ -0,0 +1,222 @@ +/* + * test-fsmonitor-client.c: client code to send commands/requests to + * a `git fsmonitor--daemon` daemon. + */ + +#include "test-tool.h" +#include "cache.h" +#include "parse-options.h" +#include "fsmonitor-ipc.h" +#include "thread-utils.h" +#include "trace2.h" + +#ifndef HAVE_FSMONITOR_DAEMON_BACKEND +int cmd__fsmonitor_client(int argc, const char **argv) +{ + die("fsmonitor--daemon not available on this platform"); +} +#else + +/* + * Read the `.git/index` to get the last token written to the + * FSMonitor Index Extension. + */ +static const char *get_token_from_index(void) +{ + struct index_state *istate = the_repository->index; + + if (do_read_index(istate, the_repository->index_file, 0) < 0) + die("unable to read index file"); + if (!istate->fsmonitor_last_update) + die("index file does not have fsmonitor extension"); + + return istate->fsmonitor_last_update; +} + +/* + * Send an IPC query to a `git-fsmonitor--daemon` daemon and + * ask for the changes since the given token or from the last + * token in the index extension. + * + * This will implicitly start a daemon process if necessary. The + * daemon process will persist after we exit. + */ +static int do_send_query(const char *token) +{ + struct strbuf answer = STRBUF_INIT; + int ret; + + if (!token || !*token) + token = get_token_from_index(); + + ret = fsmonitor_ipc__send_query(token, &answer); + if (ret < 0) + die("could not query fsmonitor--daemon"); + + write_in_full(1, answer.buf, answer.len); + strbuf_release(&answer); + + return 0; +} + +/* + * Send a "flush" command to the `git-fsmonitor--daemon` (if running) + * and tell it to flush its cache. + * + * This feature is primarily used by the test suite to simulate a loss of + * sync with the filesystem where we miss kernel events. + */ +static int do_send_flush(void) +{ + struct strbuf answer = STRBUF_INIT; + int ret; + + ret = fsmonitor_ipc__send_command("flush", &answer); + if (ret) + return ret; + + write_in_full(1, answer.buf, answer.len); + strbuf_release(&answer); + + return 0; +} + +struct hammer_thread_data +{ + pthread_t pthread_id; + int thread_nr; + + int nr_requests; + const char *token; + + int sum_successful; + int sum_errors; +}; + +static void *hammer_thread_proc(void *_hammer_thread_data) +{ + struct hammer_thread_data *data = _hammer_thread_data; + struct strbuf answer = STRBUF_INIT; + int k; + int ret; + + trace2_thread_start("hammer"); + + for (k = 0; k < data->nr_requests; k++) { + strbuf_reset(&answer); + + ret = fsmonitor_ipc__send_query(data->token, &answer); + if (ret < 0) + data->sum_errors++; + else + data->sum_successful++; + } + + strbuf_release(&answer); + trace2_thread_exit(); + return NULL; +} + +/* + * Start a pool of client threads that will each send a series of + * commands to the daemon. + * + * The goal is to overload the daemon with a sustained series of + * concurrent requests. + */ +static int do_hammer(const char *token, int nr_threads, int nr_requests) +{ + struct hammer_thread_data *data = NULL; + int k; + int sum_join_errors = 0; + int sum_commands = 0; + int sum_errors = 0; + + if (!token || !*token) + token = get_token_from_index(); + if (nr_threads < 1) + nr_threads = 1; + if (nr_requests < 1) + nr_requests = 1; + + CALLOC_ARRAY(data, nr_threads); + + for (k = 0; k < nr_threads; k++) { + struct hammer_thread_data *p = &data[k]; + p->thread_nr = k; + p->nr_requests = nr_requests; + p->token = token; + + if (pthread_create(&p->pthread_id, NULL, hammer_thread_proc, p)) { + warning("failed to create thread[%d] skipping remainder", k); + nr_threads = k; + break; + } + } + + for (k = 0; k < nr_threads; k++) { + struct hammer_thread_data *p = &data[k]; + + if (pthread_join(p->pthread_id, NULL)) + sum_join_errors++; + sum_commands += p->sum_successful; + sum_errors += p->sum_errors; + } + + fprintf(stderr, "HAMMER: [threads %d][requests %d] [ok %d][err %d][join %d]\n", + nr_threads, nr_requests, sum_commands, sum_errors, sum_join_errors); + + free(data); + + /* + * Return an error if any of the _send_query requests failed. + * We don't care about thread create/join errors. + */ + return sum_errors > 0; +} + +int cmd__fsmonitor_client(int argc, const char **argv) +{ + const char *subcmd; + const char *token = NULL; + int nr_threads = 1; + int nr_requests = 1; + + const char * const fsmonitor_client_usage[] = { + "test-tool fsmonitor-client query [<token>]", + "test-tool fsmonitor-client flush", + "test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]", + NULL, + }; + + struct option options[] = { + OPT_STRING(0, "token", &token, "token", + "command token to send to the server"), + + OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"), + OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"), + + OPT_END() + }; + + argc = parse_options(argc, argv, NULL, options, fsmonitor_client_usage, 0); + + if (argc != 1) + usage_with_options(fsmonitor_client_usage, options); + + subcmd = argv[0]; + + setup_git_directory(); + + if (!strcmp(subcmd, "query")) + return !!do_send_query(token); + + if (!strcmp(subcmd, "flush")) + return !!do_send_flush(); + + if (!strcmp(subcmd, "hammer")) + return !!do_hammer(token, nr_threads, nr_requests); + + die("Unhandled subcommand: '%s'", subcmd); +} +#endif diff --git a/t/helper/test-genzeros.c b/t/helper/test-genzeros.c index 9532f5b..8ca988d 100644 --- a/t/helper/test-genzeros.c +++ b/t/helper/test-genzeros.c @@ -3,18 +3,31 @@ int cmd__genzeros(int argc, const char **argv) { - long count; + /* static, so that it is NUL-initialized */ + static const char zeros[256 * 1024]; + intmax_t count; + ssize_t n; if (argc > 2) { fprintf(stderr, "usage: %s [<count>]\n", argv[0]); return 1; } - count = argc > 1 ? strtol(argv[1], NULL, 0) : -1L; + count = argc > 1 ? strtoimax(argv[1], NULL, 0) : -1; - while (count < 0 || count--) { - if (putchar(0) == EOF) + /* Writing out individual NUL bytes is slow... */ + while (count < 0) + if (write(1, zeros, ARRAY_SIZE(zeros)) < 0) return -1; + + while (count > 0) { + n = write(1, zeros, count < ARRAY_SIZE(zeros) ? + count : ARRAY_SIZE(zeros)); + + if (n < 0) + return -1; + + count -= n; } return 0; diff --git a/t/helper/test-getcwd.c b/t/helper/test-getcwd.c new file mode 100644 index 0000000..d680038 --- /dev/null +++ b/t/helper/test-getcwd.c @@ -0,0 +1,26 @@ +#include "test-tool.h" +#include "git-compat-util.h" +#include "parse-options.h" + +static const char *getcwd_usage[] = { + "test-tool getcwd", + NULL +}; + +int cmd__getcwd(int argc, const char **argv) +{ + struct option options[] = { + OPT_END() + }; + char *cwd; + + argc = parse_options(argc, argv, "test-tools", options, getcwd_usage, 0); + if (argc > 0) + usage_with_options(getcwd_usage, options); + + cwd = xgetcwd(); + puts(cwd); + free(cwd); + + return 0; +} diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c index 432233c..f40d9ad 100644 --- a/t/helper/test-hash-speed.c +++ b/t/helper/test-hash-speed.c @@ -57,5 +57,5 @@ int cmd__hash_speed(int ac, const char **av) free(p); } - exit(0); + return 0; } diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c index 0a31de6..261c545 100644 --- a/t/helper/test-hash.c +++ b/t/helper/test-hash.c @@ -54,5 +54,5 @@ int cmd_hash_impl(int ac, const char **av, int algo) fwrite(hash, 1, algop->rawsz, stdout); else puts(hash_to_hex_algop(hash, algop)); - exit(0); + return 0; } diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c index f387062..36ff07b 100644 --- a/t/helper/test-hashmap.c +++ b/t/helper/test-hashmap.c @@ -110,7 +110,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) hashmap_add(&map, &entries[i]->ent); } - hashmap_free(&map); + hashmap_clear(&map); } } else { /* test map lookups */ @@ -130,7 +130,7 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) } } - hashmap_free(&map); + hashmap_clear(&map); } } @@ -151,12 +151,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) int cmd__hashmap(int argc, const char **argv) { struct strbuf line = STRBUF_INIT; - struct hashmap map; int icase; + struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase); /* init hash map */ icase = argc > 1 && !strcmp("ignorecase", argv[1]); - hashmap_init(&map, test_entry_cmp, &icase, 0); /* process commands from stdin */ while (strbuf_getline(&line, stdin) != EOF) { @@ -262,6 +261,6 @@ int cmd__hashmap(int argc, const char **argv) } strbuf_release(&line); - hashmap_free_entries(&map, struct test_entry, ent); + hashmap_clear_and_free(&map, struct test_entry, ent); return 0; } diff --git a/t/helper/test-hexdump.c b/t/helper/test-hexdump.c new file mode 100644 index 0000000..811e89c --- /dev/null +++ b/t/helper/test-hexdump.c @@ -0,0 +1,30 @@ +#include "test-tool.h" +#include "git-compat-util.h" + +/* + * Read stdin and print a hexdump to stdout. + */ +int cmd__hexdump(int argc, const char **argv) +{ + char buf[1024]; + ssize_t i, len; + int have_data = 0; + + for (;;) { + len = xread(0, buf, sizeof(buf)); + if (len < 0) + die_errno("failure reading stdin"); + if (!len) + break; + + have_data = 1; + + for (i = 0; i < len; i++) + printf("%02x ", (unsigned char)buf[i]); + } + + if (have_data) + putchar('\n'); + + return 0; +} diff --git a/t/helper/test-line-buffer.c b/t/helper/test-line-buffer.c deleted file mode 100644 index 078dd7e..0000000 --- a/t/helper/test-line-buffer.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * test-line-buffer.c: code to exercise the svn importer's input helper - */ - -#include "git-compat-util.h" -#include "strbuf.h" -#include "vcs-svn/line_buffer.h" - -static uint32_t strtouint32(const char *s) -{ - char *end; - uintmax_t n = strtoumax(s, &end, 10); - if (*s == '\0' || *end != '\0') - die("invalid count: %s", s); - return (uint32_t) n; -} - -static void handle_command(const char *command, const char *arg, struct line_buffer *buf) -{ - if (starts_with(command, "binary ")) { - struct strbuf sb = STRBUF_INIT; - strbuf_addch(&sb, '>'); - buffer_read_binary(buf, &sb, strtouint32(arg)); - fwrite(sb.buf, 1, sb.len, stdout); - strbuf_release(&sb); - } else if (starts_with(command, "copy ")) { - buffer_copy_bytes(buf, strtouint32(arg)); - } else if (starts_with(command, "skip ")) { - buffer_skip_bytes(buf, strtouint32(arg)); - } else { - die("unrecognized command: %s", command); - } -} - -static void handle_line(const char *line, struct line_buffer *stdin_buf) -{ - const char *arg = strchr(line, ' '); - if (!arg) - die("no argument in line: %s", line); - handle_command(line, arg + 1, stdin_buf); -} - -int cmd_main(int argc, const char **argv) -{ - struct line_buffer stdin_buf = LINE_BUFFER_INIT; - struct line_buffer file_buf = LINE_BUFFER_INIT; - struct line_buffer *input = &stdin_buf; - const char *filename; - char *s; - - if (argc == 1) - filename = NULL; - else if (argc == 2) - filename = argv[1]; - else - usage("test-line-buffer [file | &fd] < script"); - - if (buffer_init(&stdin_buf, NULL)) - die_errno("open error"); - if (filename) { - if (*filename == '&') { - if (buffer_fdinit(&file_buf, strtouint32(filename + 1))) - die_errno("error opening fd %s", filename + 1); - } else { - if (buffer_init(&file_buf, filename)) - die_errno("error opening %s", filename); - } - input = &file_buf; - } - - while ((s = buffer_read_line(&stdin_buf))) - handle_line(s, input); - - if (filename && buffer_deinit(&file_buf)) - die("error reading from %s", filename); - if (buffer_deinit(&stdin_buf)) - die("input error"); - if (ferror(stdout)) - die("output error"); - return 0; -} diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c index b9fd427..4079fde 100644 --- a/t/helper/test-match-trees.c +++ b/t/helper/test-match-trees.c @@ -23,5 +23,5 @@ int cmd__match_trees(int ac, const char **av) shift_tree(the_repository, &one->object.oid, &two->object.oid, &shifted, -1); printf("shifted: %s\n", oid_to_hex(&shifted)); - exit(0); + return 0; } diff --git a/t/helper/test-mergesort.c b/t/helper/test-mergesort.c index c5cffaa..ebf68f7 100644 --- a/t/helper/test-mergesort.c +++ b/t/helper/test-mergesort.c @@ -2,6 +2,12 @@ #include "cache.h" #include "mergesort.h" +static uint32_t minstd_rand(uint32_t *state) +{ + *state = (uint64_t)*state * 48271 % 2147483647; + return *state; +} + struct line { char *text; struct line *next; @@ -23,14 +29,12 @@ static int compare_strings(const void *a, const void *b) return strcmp(x->text, y->text); } -int cmd__mergesort(int argc, const char **argv) +static int sort_stdin(void) { struct line *line, *p = NULL, *lines = NULL; struct strbuf sb = STRBUF_INIT; - for (;;) { - if (strbuf_getwholeline(&sb, stdin, '\n')) - break; + while (!strbuf_getline(&sb, stdin)) { line = xmalloc(sizeof(struct line)); line->text = strbuf_detach(&sb, NULL); if (p) { @@ -46,8 +50,362 @@ int cmd__mergesort(int argc, const char **argv) lines = llist_mergesort(lines, get_next, set_next, compare_strings); while (lines) { - printf("%s", lines->text); + puts(lines->text); lines = lines->next; } return 0; } + +static void dist_sawtooth(int *arr, int n, int m) +{ + int i; + for (i = 0; i < n; i++) + arr[i] = i % m; +} + +static void dist_rand(int *arr, int n, int m) +{ + int i; + uint32_t seed = 1; + for (i = 0; i < n; i++) + arr[i] = minstd_rand(&seed) % m; +} + +static void dist_stagger(int *arr, int n, int m) +{ + int i; + for (i = 0; i < n; i++) + arr[i] = (i * m + i) % n; +} + +static void dist_plateau(int *arr, int n, int m) +{ + int i; + for (i = 0; i < n; i++) + arr[i] = (i < m) ? i : m; +} + +static void dist_shuffle(int *arr, int n, int m) +{ + int i, j, k; + uint32_t seed = 1; + for (i = j = 0, k = 1; i < n; i++) + arr[i] = minstd_rand(&seed) % m ? (j += 2) : (k += 2); +} + +#define DIST(name) { #name, dist_##name } + +static struct dist { + const char *name; + void (*fn)(int *arr, int n, int m); +} dist[] = { + DIST(sawtooth), + DIST(rand), + DIST(stagger), + DIST(plateau), + DIST(shuffle), +}; + +static const struct dist *get_dist_by_name(const char *name) +{ + int i; + for (i = 0; i < ARRAY_SIZE(dist); i++) { + if (!strcmp(dist[i].name, name)) + return &dist[i]; + } + return NULL; +} + +static void mode_copy(int *arr, int n) +{ + /* nothing */ +} + +static void mode_reverse(int *arr, int n) +{ + int i, j; + for (i = 0, j = n - 1; i < j; i++, j--) + SWAP(arr[i], arr[j]); +} + +static void mode_reverse_1st_half(int *arr, int n) +{ + mode_reverse(arr, n / 2); +} + +static void mode_reverse_2nd_half(int *arr, int n) +{ + int half = n / 2; + mode_reverse(arr + half, n - half); +} + +static int compare_ints(const void *av, const void *bv) +{ + const int *ap = av, *bp = bv; + int a = *ap, b = *bp; + return (a > b) - (a < b); +} + +static void mode_sort(int *arr, int n) +{ + QSORT(arr, n, compare_ints); +} + +static void mode_dither(int *arr, int n) +{ + int i; + for (i = 0; i < n; i++) + arr[i] += i % 5; +} + +static void unriffle(int *arr, int n, int *tmp) +{ + int i, j; + COPY_ARRAY(tmp, arr, n); + for (i = j = 0; i < n; i += 2) + arr[j++] = tmp[i]; + for (i = 1; i < n; i += 2) + arr[j++] = tmp[i]; +} + +static void unriffle_recursively(int *arr, int n, int *tmp) +{ + if (n > 1) { + int half = n / 2; + unriffle(arr, n, tmp); + unriffle_recursively(arr, half, tmp); + unriffle_recursively(arr + half, n - half, tmp); + } +} + +static void mode_unriffle(int *arr, int n) +{ + int *tmp; + ALLOC_ARRAY(tmp, n); + unriffle_recursively(arr, n, tmp); + free(tmp); +} + +static unsigned int prev_pow2(unsigned int n) +{ + unsigned int pow2 = 1; + while (pow2 * 2 < n) + pow2 *= 2; + return pow2; +} + +static void unriffle_recursively_skewed(int *arr, int n, int *tmp) +{ + if (n > 1) { + int pow2 = prev_pow2(n); + int rest = n - pow2; + unriffle(arr + pow2 - rest, rest * 2, tmp); + unriffle_recursively_skewed(arr, pow2, tmp); + unriffle_recursively_skewed(arr + pow2, rest, tmp); + } +} + +static void mode_unriffle_skewed(int *arr, int n) +{ + int *tmp; + ALLOC_ARRAY(tmp, n); + unriffle_recursively_skewed(arr, n, tmp); + free(tmp); +} + +#define MODE(name) { #name, mode_##name } + +static struct mode { + const char *name; + void (*fn)(int *arr, int n); +} mode[] = { + MODE(copy), + MODE(reverse), + MODE(reverse_1st_half), + MODE(reverse_2nd_half), + MODE(sort), + MODE(dither), + MODE(unriffle), + MODE(unriffle_skewed), +}; + +static const struct mode *get_mode_by_name(const char *name) +{ + int i; + for (i = 0; i < ARRAY_SIZE(mode); i++) { + if (!strcmp(mode[i].name, name)) + return &mode[i]; + } + return NULL; +} + +static int generate(int argc, const char **argv) +{ + const struct dist *dist = NULL; + const struct mode *mode = NULL; + int i, n, m, *arr; + + if (argc != 4) + return 1; + + dist = get_dist_by_name(argv[0]); + mode = get_mode_by_name(argv[1]); + n = strtol(argv[2], NULL, 10); + m = strtol(argv[3], NULL, 10); + if (!dist || !mode) + return 1; + + ALLOC_ARRAY(arr, n); + dist->fn(arr, n, m); + mode->fn(arr, n); + for (i = 0; i < n; i++) + printf("%08x\n", arr[i]); + free(arr); + return 0; +} + +static struct stats { + int get_next, set_next, compare; +} stats; + +struct number { + int value, rank; + struct number *next; +}; + +static void *get_next_number(const void *a) +{ + stats.get_next++; + return ((const struct number *)a)->next; +} + +static void set_next_number(void *a, void *b) +{ + stats.set_next++; + ((struct number *)a)->next = b; +} + +static int compare_numbers(const void *av, const void *bv) +{ + const struct number *an = av, *bn = bv; + int a = an->value, b = bn->value; + stats.compare++; + return (a > b) - (a < b); +} + +static void clear_numbers(struct number *list) +{ + while (list) { + struct number *next = list->next; + free(list); + list = next; + } +} + +static int test(const struct dist *dist, const struct mode *mode, int n, int m) +{ + int *arr; + size_t i; + struct number *curr, *list, **tail; + int is_sorted = 1; + int is_stable = 1; + const char *verdict; + int result = -1; + + ALLOC_ARRAY(arr, n); + dist->fn(arr, n, m); + mode->fn(arr, n); + for (i = 0, tail = &list; i < n; i++) { + curr = xmalloc(sizeof(*curr)); + curr->value = arr[i]; + curr->rank = i; + *tail = curr; + tail = &curr->next; + } + *tail = NULL; + + stats.get_next = stats.set_next = stats.compare = 0; + list = llist_mergesort(list, get_next_number, set_next_number, + compare_numbers); + + QSORT(arr, n, compare_ints); + for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) { + if (arr[i] != curr->value) + is_sorted = 0; + if (curr->next && curr->value == curr->next->value && + curr->rank >= curr->next->rank) + is_stable = 0; + } + if (i < n) { + verdict = "too short"; + } else if (curr) { + verdict = "too long"; + } else if (!is_sorted) { + verdict = "not sorted"; + } else if (!is_stable) { + verdict = "unstable"; + } else { + verdict = "OK"; + result = 0; + } + + printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n", + dist->name, mode->name, n, m, stats.get_next, stats.set_next, + stats.compare, verdict); + + clear_numbers(list); + free(arr); + + return result; +} + +/* + * A version of the qsort certification program from "Engineering a Sort + * Function" by Bentley and McIlroy, Software—Practice and Experience, + * Volume 23, Issue 11, 1249–1265 (November 1993). + */ +static int run_tests(int argc, const char **argv) +{ + const char *argv_default[] = { "100", "1023", "1024", "1025" }; + if (!argc) + return run_tests(ARRAY_SIZE(argv_default), argv_default); + printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n", + "distribut", "mode", "n", "m", "get_next", "set_next", + "compare", "verdict"); + while (argc--) { + int i, j, m, n = strtol(*argv++, NULL, 10); + for (i = 0; i < ARRAY_SIZE(dist); i++) { + for (j = 0; j < ARRAY_SIZE(mode); j++) { + for (m = 1; m < 2 * n; m *= 2) { + if (test(&dist[i], &mode[j], n, m)) + return 1; + } + } + } + } + return 0; +} + +int cmd__mergesort(int argc, const char **argv) +{ + int i; + const char *sep; + + if (argc == 6 && !strcmp(argv[1], "generate")) + return generate(argc - 2, argv + 2); + if (argc == 2 && !strcmp(argv[1], "sort")) + return sort_stdin(); + if (argc > 1 && !strcmp(argv[1], "test")) + return run_tests(argc - 2, argv + 2); + fprintf(stderr, "usage: test-tool mergesort generate <distribution> <mode> <n> <m>\n"); + fprintf(stderr, " or: test-tool mergesort sort\n"); + fprintf(stderr, " or: test-tool mergesort test [<n>...]\n"); + fprintf(stderr, "\n"); + for (i = 0, sep = "distributions: "; i < ARRAY_SIZE(dist); i++, sep = ", ") + fprintf(stderr, "%s%s", sep, dist[i].name); + fprintf(stderr, "\n"); + for (i = 0, sep = "modes: "; i < ARRAY_SIZE(mode); i++, sep = ", ") + fprintf(stderr, "%s%s", sep, mode[i].name); + fprintf(stderr, "\n"); + return 129; +} diff --git a/t/helper/test-sha1-array.c b/t/helper/test-oid-array.c index ad5e69f..d1324d0 100644 --- a/t/helper/test-sha1-array.c +++ b/t/helper/test-oid-array.c @@ -1,6 +1,6 @@ #include "test-tool.h" #include "cache.h" -#include "sha1-array.h" +#include "oid-array.h" static int print_oid(const struct object_id *oid, void *data) { @@ -8,10 +8,13 @@ static int print_oid(const struct object_id *oid, void *data) return 0; } -int cmd__sha1_array(int argc, const char **argv) +int cmd__oid_array(int argc, const char **argv) { struct oid_array array = OID_ARRAY_INIT; struct strbuf line = STRBUF_INIT; + int nongit_ok; + + setup_git_directory_gently(&nongit_ok); while (strbuf_getline(&line, stdin) != EOF) { const char *arg; @@ -19,11 +22,11 @@ int cmd__sha1_array(int argc, const char **argv) if (skip_prefix(line.buf, "append ", &arg)) { if (get_oid_hex(arg, &oid)) - die("not a hexadecimal SHA1: %s", arg); + die("not a hexadecimal oid: %s", arg); oid_array_append(&array, &oid); } else if (skip_prefix(line.buf, "lookup ", &arg)) { if (get_oid_hex(arg, &oid)) - die("not a hexadecimal SHA1: %s", arg); + die("not a hexadecimal oid: %s", arg); printf("%d\n", oid_array_lookup(&array, &oid)); } else if (!strcmp(line.buf, "clear")) oid_array_clear(&array); @@ -32,5 +35,9 @@ int cmd__sha1_array(int argc, const char **argv) else die("unknown command: %s", line.buf); } + + strbuf_release(&line); + oid_array_clear(&array); + return 0; } diff --git a/t/helper/test-oidtree.c b/t/helper/test-oidtree.c new file mode 100644 index 0000000..d48a409 --- /dev/null +++ b/t/helper/test-oidtree.c @@ -0,0 +1,52 @@ +#include "test-tool.h" +#include "cache.h" +#include "oidtree.h" + +static enum cb_next print_oid(const struct object_id *oid, void *data) +{ + puts(oid_to_hex(oid)); + return CB_CONTINUE; +} + +int cmd__oidtree(int argc, const char **argv) +{ + struct oidtree ot; + struct strbuf line = STRBUF_INIT; + int nongit_ok; + int algo = GIT_HASH_UNKNOWN; + + oidtree_init(&ot); + setup_git_directory_gently(&nongit_ok); + + while (strbuf_getline(&line, stdin) != EOF) { + const char *arg; + struct object_id oid; + + if (skip_prefix(line.buf, "insert ", &arg)) { + if (get_oid_hex_any(arg, &oid) == GIT_HASH_UNKNOWN) + die("insert not a hexadecimal oid: %s", arg); + algo = oid.algo; + oidtree_insert(&ot, &oid); + } else if (skip_prefix(line.buf, "contains ", &arg)) { + if (get_oid_hex(arg, &oid)) + die("contains not a hexadecimal oid: %s", arg); + printf("%d\n", oidtree_contains(&ot, &oid)); + } else if (skip_prefix(line.buf, "each ", &arg)) { + char buf[GIT_MAX_HEXSZ + 1] = { '0' }; + memset(&oid, 0, sizeof(oid)); + memcpy(buf, arg, strlen(arg)); + buf[hash_algos[algo].hexsz] = '\0'; + get_oid_hex_any(buf, &oid); + oid.algo = algo; + oidtree_each(&ot, &oid, strlen(arg), print_oid, NULL); + } else if (!strcmp(line.buf, "clear")) { + oidtree_clear(&ot); + } else { + die("unknown command: %s", line.buf); + } + } + + strbuf_release(&line); + + return 0; +} diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c new file mode 100644 index 0000000..f7b79da --- /dev/null +++ b/t/helper/test-pack-mtimes.c @@ -0,0 +1,56 @@ +#include "git-compat-util.h" +#include "test-tool.h" +#include "strbuf.h" +#include "object-store.h" +#include "packfile.h" +#include "pack-mtimes.h" + +static void dump_mtimes(struct packed_git *p) +{ + uint32_t i; + if (load_pack_mtimes(p) < 0) + die("could not load pack .mtimes"); + + for (i = 0; i < p->num_objects; i++) { + struct object_id oid; + if (nth_packed_object_id(&oid, p, i) < 0) + die("could not load object id at position %"PRIu32, i); + + printf("%s %"PRIu32"\n", + oid_to_hex(&oid), nth_packed_mtime(p, i)); + } +} + +static const char *pack_mtimes_usage = "\n" +" test-tool pack-mtimes <pack-name.mtimes>"; + +int cmd__pack_mtimes(int argc, const char **argv) +{ + struct strbuf buf = STRBUF_INIT; + struct packed_git *p; + + setup_git_directory(); + + if (argc != 2) + usage(pack_mtimes_usage); + + for (p = get_all_packs(the_repository); p; p = p->next) { + strbuf_addstr(&buf, basename(p->pack_name)); + strbuf_strip_suffix(&buf, ".pack"); + strbuf_addstr(&buf, ".mtimes"); + + if (!strcmp(buf.buf, argv[1])) + break; + + strbuf_reset(&buf); + } + + strbuf_release(&buf); + + if (!p) + die("could not find pack '%s'", argv[1]); + + dump_mtimes(p); + + return 0; +} diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index af82db0..48d3cf6 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -14,7 +14,6 @@ static int dry_run = 0, quiet = 0; static char *string = NULL; static char *file = NULL; static int ambiguous; -static struct string_list list = STRING_LIST_INIT_NODUP; static struct { int called; @@ -107,6 +106,8 @@ int cmd__parse_options(int argc, const char **argv) NULL }; struct string_list expect = STRING_LIST_INIT_NODUP; + struct string_list list = STRING_LIST_INIT_NODUP; + struct option options[] = { OPT_BOOL(0, "yes", &boolean, "get a boolean"), OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), @@ -121,6 +122,8 @@ int cmd__parse_options(int argc, const char **argv) OPT_INTEGER('j', NULL, &integer, "get a integer, too"), OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), + OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1), + OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2), OPT_CALLBACK('L', "length", &integer, "str", "get length of <str>", length_callback), OPT_FILENAME('F', "file", &file, "set file to <file>"), @@ -132,7 +135,6 @@ int cmd__parse_options(int argc, const char **argv) OPT_NOOP_NOARG(0, "obsolete"), OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), OPT_GROUP("Magic arguments"), - OPT_ARGUMENT("quux", NULL, "means --quux"), OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", number_callback), { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", @@ -184,5 +186,9 @@ int cmd__parse_options(int argc, const char **argv) for (i = 0; i < argc; i++) show(&expect, &ret, "arg %02d: %s", i, argv[i]); + expect.strdup_strings = 1; + string_list_clear(&expect, 0); + string_list_clear(&list, 0); + return ret; } diff --git a/t/helper/test-parse-pathspec-file.c b/t/helper/test-parse-pathspec-file.c new file mode 100644 index 0000000..b3e08ce --- /dev/null +++ b/t/helper/test-parse-pathspec-file.c @@ -0,0 +1,33 @@ +#include "test-tool.h" +#include "parse-options.h" +#include "pathspec.h" +#include "gettext.h" + +int cmd__parse_pathspec_file(int argc, const char **argv) +{ + struct pathspec pathspec; + const char *pathspec_from_file = NULL; + int pathspec_file_nul = 0, i; + + static const char *const usage[] = { + "test-tool parse-pathspec-file --pathspec-from-file [--pathspec-file-nul]", + NULL + }; + + struct option options[] = { + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), + OPT_END() + }; + + parse_options(argc, argv, NULL, options, usage, 0); + + parse_pathspec_file(&pathspec, 0, 0, NULL, pathspec_from_file, + pathspec_file_nul); + + for (i = 0; i < pathspec.nr; i++) + printf("%s\n", pathspec.items[i].original); + + clear_pathspec(&pathspec); + return 0; +} diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c new file mode 100644 index 0000000..3f102cf --- /dev/null +++ b/t/helper/test-partial-clone.c @@ -0,0 +1,43 @@ +#include "cache.h" +#include "test-tool.h" +#include "repository.h" +#include "object-store.h" + +/* + * Prints the size of the object corresponding to the given hash in a specific + * gitdir. This is similar to "git -C gitdir cat-file -s", except that this + * exercises the code that accesses the object of an arbitrary repository that + * is not the_repository. ("git -C gitdir" makes it so that the_repository is + * the one in gitdir.) + */ +static void object_info(const char *gitdir, const char *oid_hex) +{ + struct repository r; + struct object_id oid; + unsigned long size; + struct object_info oi = {.sizep = &size}; + const char *p; + + if (repo_init(&r, gitdir, NULL)) + die("could not init repo"); + if (parse_oid_hex(oid_hex, &oid, &p)) + die("could not parse oid"); + if (oid_object_info_extended(&r, &oid, &oi, 0)) + die("could not obtain object info"); + printf("%d\n", (int) size); +} + +int cmd__partial_clone(int argc, const char **argv) +{ + setup_git_directory(); + + if (argc < 4) + die("too few arguments"); + + if (!strcmp(argv[1], "object-info")) + object_info(argv[2], argv[3]); + else + die("invalid argument '%s'", argv[1]); + + return 0; +} diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index 409034c..229ed41 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c @@ -172,9 +172,22 @@ static struct test_data dirname_data[] = { { NULL, NULL } }; -static int is_dotgitmodules(const char *path) +static int check_dotfile(const char *x, const char **argv, + int (*is_hfs)(const char *), + int (*is_ntfs)(const char *)) { - return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path); + int res = 0, expect = 1; + for (; *argv; argv++) { + if (!strcmp("--not", *argv)) + expect = !expect; + else if (expect != (is_hfs(*argv) || is_ntfs(*argv))) + res = error("'%s' is %s.git%s", *argv, + expect ? "not " : "", x); + else + fprintf(stderr, "ok: '%s' is %s.git%s\n", + *argv, expect ? "" : "not ", x); + } + return !!res; } static int cmp_by_st_size(const void *a, const void *b) @@ -290,11 +303,14 @@ int cmd__path_utils(int argc, const char **argv) } if (argc >= 2 && !strcmp(argv[1], "real_path")) { + struct strbuf realpath = STRBUF_INIT; while (argc > 2) { - puts(real_path(argv[2])); + strbuf_realpath(&realpath, argv[2], 1); + puts(realpath.buf); argc--; argv++; } + strbuf_release(&realpath); return 0; } @@ -379,17 +395,24 @@ int cmd__path_utils(int argc, const char **argv) return test_function(dirname_data, posix_dirname, argv[1]); if (argc > 2 && !strcmp(argv[1], "is_dotgitmodules")) { - int res = 0, expect = 1, i; - for (i = 2; i < argc; i++) - if (!strcmp("--not", argv[i])) - expect = !expect; - else if (expect != is_dotgitmodules(argv[i])) - res = error("'%s' is %s.gitmodules", argv[i], - expect ? "not " : ""); - else - fprintf(stderr, "ok: '%s' is %s.gitmodules\n", - argv[i], expect ? "" : "not "); - return !!res; + return check_dotfile("modules", argv + 2, + is_hfs_dotgitmodules, + is_ntfs_dotgitmodules); + } + if (argc > 2 && !strcmp(argv[1], "is_dotgitignore")) { + return check_dotfile("ignore", argv + 2, + is_hfs_dotgitignore, + is_ntfs_dotgitignore); + } + if (argc > 2 && !strcmp(argv[1], "is_dotgitattributes")) { + return check_dotfile("attributes", argv + 2, + is_hfs_dotgitattributes, + is_ntfs_dotgitattributes); + } + if (argc > 2 && !strcmp(argv[1], "is_dotmailmap")) { + return check_dotfile("mailmap", argv + 2, + is_hfs_dotmailmap, + is_ntfs_dotmailmap); } if (argc > 2 && !strcmp(argv[1], "file-size")) { diff --git a/t/helper/test-pcre2-config.c b/t/helper/test-pcre2-config.c new file mode 100644 index 0000000..5258fdd --- /dev/null +++ b/t/helper/test-pcre2-config.c @@ -0,0 +1,12 @@ +#include "test-tool.h" +#include "cache.h" +#include "grep.h" + +int cmd__pcre2_config(int argc, const char **argv) +{ + if (argc == 2 && !strcmp(argv[1], "has-PCRE2_MATCH_INVALID_UTF")) { + int value = PCRE2_MATCH_INVALID_UTF; + return !value; + } + return 1; +} diff --git a/t/helper/test-pkt-line.c b/t/helper/test-pkt-line.c index 282d536..c5e052e 100644 --- a/t/helper/test-pkt-line.c +++ b/t/helper/test-pkt-line.c @@ -26,6 +26,16 @@ static void pack(int argc, const char **argv) } } +static void pack_raw_stdin(void) +{ + struct strbuf sb = STRBUF_INIT; + + if (strbuf_read(&sb, 0, 0) < 0) + die_errno("failed to read from stdin"); + packet_write(1, sb.buf, sb.len); + strbuf_release(&sb); +} + static void unpack(void) { struct packet_reader reader; @@ -46,6 +56,9 @@ static void unpack(void) case PACKET_READ_DELIM: printf("0001\n"); break; + case PACKET_READ_RESPONSE_END: + printf("0002\n"); + break; } } } @@ -67,7 +80,7 @@ static void unpack_sideband(void) case PACKET_READ_NORMAL: band = reader.line[0] & 0xff; if (band < 1 || band > 2) - die("unexpected side band %d", band); + continue; /* skip non-sideband packets */ fd = band; write_or_die(fd, reader.line + 1, reader.pktlen - 1); @@ -75,11 +88,31 @@ static void unpack_sideband(void) case PACKET_READ_FLUSH: return; case PACKET_READ_DELIM: + case PACKET_READ_RESPONSE_END: break; } } } +static int send_split_sideband(void) +{ + const char *part1 = "Hello,"; + const char *primary = "\001primary: regular output\n"; + const char *part2 = " world!\n"; + + send_sideband(1, 2, part1, strlen(part1), LARGE_PACKET_MAX); + packet_write(1, primary, strlen(primary)); + send_sideband(1, 2, part2, strlen(part2), LARGE_PACKET_MAX); + packet_response_end(1); + + return 0; +} + +static int receive_sideband(void) +{ + return recv_sideband("sideband", 0, 1); +} + int cmd__pkt_line(int argc, const char **argv) { if (argc < 2) @@ -87,10 +120,16 @@ int cmd__pkt_line(int argc, const char **argv) if (!strcmp(argv[1], "pack")) pack(argc - 2, argv + 2); + else if (!strcmp(argv[1], "pack-raw-stdin")) + pack_raw_stdin(); else if (!strcmp(argv[1], "unpack")) unpack(); else if (!strcmp(argv[1], "unpack-sideband")) unpack_sideband(); + else if (!strcmp(argv[1], "send-split-sideband")) + send_split_sideband(); + else if (!strcmp(argv[1], "receive-sideband")) + receive_sideband(); else die("invalid argument '%s'", argv[1]); diff --git a/t/helper/test-prio-queue.c b/t/helper/test-prio-queue.c index f402844..133b5e6 100644 --- a/t/helper/test-prio-queue.c +++ b/t/helper/test-prio-queue.c @@ -46,5 +46,7 @@ int cmd__prio_queue(int argc, const char **argv) } } + clear_prio_queue(&pq); + return 0; } diff --git a/t/helper/test-proc-receive.c b/t/helper/test-proc-receive.c new file mode 100644 index 0000000..cc08506 --- /dev/null +++ b/t/helper/test-proc-receive.c @@ -0,0 +1,202 @@ +#include "cache.h" +#include "connect.h" +#include "parse-options.h" +#include "pkt-line.h" +#include "sigchain.h" +#include "test-tool.h" + +static const char *proc_receive_usage[] = { + "test-tool proc-receive [<options>...]", + NULL +}; + +static int die_read_version; +static int die_write_version; +static int die_read_commands; +static int die_read_push_options; +static int die_write_report; +static int no_push_options; +static int use_atomic; +static int use_push_options; +static int verbose; +static int version = 1; +static struct string_list returns = STRING_LIST_INIT_NODUP; + +struct command { + struct command *next; + const char *error_string; + unsigned int skip_update:1, + did_not_exist:1; + int index; + struct object_id old_oid; + struct object_id new_oid; + char ref_name[FLEX_ARRAY]; /* more */ +}; + +static void proc_receive_verison(struct packet_reader *reader) { + int server_version = 0; + + if (die_read_version) + die("die with the --die-read-version option"); + + for (;;) { + int linelen; + + if (packet_reader_read(reader) != PACKET_READ_NORMAL) + break; + + /* Ignore version negotiation for version 0 */ + if (version == 0) + continue; + + if (reader->pktlen > 8 && starts_with(reader->line, "version=")) { + server_version = atoi(reader->line+8); + if (server_version != 1) + die("bad protocol version: %d", server_version); + linelen = strlen(reader->line); + if (linelen < reader->pktlen) { + const char *feature_list = reader->line + linelen + 1; + if (parse_feature_request(feature_list, "atomic")) + use_atomic= 1; + if (parse_feature_request(feature_list, "push-options")) + use_push_options = 1; + } + } + } + + if (die_write_version) + die("die with the --die-write-version option"); + + if (version != 0) + packet_write_fmt(1, "version=%d%c%s\n", + version, '\0', + use_push_options && !no_push_options ? "push-options": ""); + packet_flush(1); +} + +static void proc_receive_read_commands(struct packet_reader *reader, + struct command **commands) +{ + struct command **tail = commands; + + for (;;) { + struct object_id old_oid, new_oid; + struct command *cmd; + const char *refname; + const char *p; + + if (packet_reader_read(reader) != PACKET_READ_NORMAL) + break; + + if (die_read_commands) + die("die with the --die-read-commands option"); + + if (parse_oid_hex(reader->line, &old_oid, &p) || + *p++ != ' ' || + parse_oid_hex(p, &new_oid, &p) || + *p++ != ' ') + die("protocol error: expected 'old new ref', got '%s'", + reader->line); + refname = p; + FLEX_ALLOC_STR(cmd, ref_name, refname); + oidcpy(&cmd->old_oid, &old_oid); + oidcpy(&cmd->new_oid, &new_oid); + + *tail = cmd; + tail = &cmd->next; + } +} + +static void proc_receive_read_push_options(struct packet_reader *reader, + struct string_list *options) +{ + + if (no_push_options || !use_push_options) + return; + + if (die_read_push_options) + die("die with the --die-read-push-options option"); + + while (1) { + if (packet_reader_read(reader) != PACKET_READ_NORMAL) + break; + + string_list_append(options, reader->line); + } +} + +int cmd__proc_receive(int argc, const char **argv) +{ + int nongit_ok = 0; + struct packet_reader reader; + struct command *commands = NULL; + struct string_list push_options = STRING_LIST_INIT_DUP; + struct string_list_item *item; + struct option options[] = { + OPT_BOOL(0, "no-push-options", &no_push_options, + "disable push options"), + OPT_BOOL(0, "die-read-version", &die_read_version, + "die when reading version"), + OPT_BOOL(0, "die-write-version", &die_write_version, + "die when writing version"), + OPT_BOOL(0, "die-read-commands", &die_read_commands, + "die when reading commands"), + OPT_BOOL(0, "die-read-push-options", &die_read_push_options, + "die when reading push-options"), + OPT_BOOL(0, "die-write-report", &die_write_report, + "die when writing report"), + OPT_STRING_LIST('r', "return", &returns, "old/new/ref/status/msg", + "return of results"), + OPT__VERBOSE(&verbose, "be verbose"), + OPT_INTEGER('V', "version", &version, + "use this protocol version number"), + OPT_END() + }; + + setup_git_directory_gently(&nongit_ok); + + argc = parse_options(argc, argv, "test-tools", options, proc_receive_usage, 0); + if (argc > 0) + usage_msg_opt("Too many arguments.", proc_receive_usage, options); + packet_reader_init(&reader, 0, NULL, 0, + PACKET_READ_CHOMP_NEWLINE | + PACKET_READ_GENTLE_ON_EOF); + + sigchain_push(SIGPIPE, SIG_IGN); + proc_receive_verison(&reader); + proc_receive_read_commands(&reader, &commands); + proc_receive_read_push_options(&reader, &push_options); + + if (verbose) { + struct command *cmd; + + if (use_push_options || use_atomic) + fprintf(stderr, "proc-receive:%s%s\n", + use_atomic? " atomic": "", + use_push_options ? " push_options": ""); + + for (cmd = commands; cmd; cmd = cmd->next) + fprintf(stderr, "proc-receive< %s %s %s\n", + oid_to_hex(&cmd->old_oid), + oid_to_hex(&cmd->new_oid), + cmd->ref_name); + + if (push_options.nr > 0) + for_each_string_list_item(item, &push_options) + fprintf(stderr, "proc-receive< %s\n", item->string); + + if (returns.nr) + for_each_string_list_item(item, &returns) + fprintf(stderr, "proc-receive> %s\n", item->string); + } + + if (die_write_report) + die("die with the --die-write-report option"); + if (returns.nr) + for_each_string_list_item(item, &returns) + packet_write_fmt(1, "%s\n", item->string); + packet_flush(1); + sigchain_pop(SIGPIPE); + + return 0; +} diff --git a/t/helper/test-progress.c b/t/helper/test-progress.c index 42b96cb..6cc9735 100644 --- a/t/helper/test-progress.c +++ b/t/helper/test-progress.c @@ -3,6 +3,9 @@ * * Reads instructions from standard input, one instruction per line: * + * "start <total>[ <title>]" - Call start_progress(title, total), + * Uses the default title of "Working hard" + * if the " <title>" is omitted. * "progress <items>" - Call display_progress() with the given item count * as parameter. * "throughput <bytes> <millis> - Call display_throughput() with the given @@ -10,50 +13,60 @@ * specify the time elapsed since the * start_progress() call. * "update" - Set the 'progress_update' flag. + * "stop" - Call stop_progress(). * * See 't0500-progress-display.sh' for examples. */ +#define GIT_TEST_PROGRESS_ONLY #include "test-tool.h" #include "gettext.h" #include "parse-options.h" #include "progress.h" #include "strbuf.h" - -/* - * These are defined in 'progress.c', but are not exposed in 'progress.h', - * because they are exclusively for testing. - */ -extern int progress_testing; -extern uint64_t progress_test_ns; -void progress_test_force_update(void); +#include "string-list.h" int cmd__progress(int argc, const char **argv) { - int total = 0; - const char *title; + const char *const default_title = "Working hard"; + struct string_list titles = STRING_LIST_INIT_DUP; struct strbuf line = STRBUF_INIT; - struct progress *progress; + struct progress *progress = NULL; const char *usage[] = { - "test-tool progress [--total=<n>] <progress-title>", + "test-tool progress <stdin", NULL }; struct option options[] = { - OPT_INTEGER(0, "total", &total, "total number of items"), OPT_END(), }; argc = parse_options(argc, argv, NULL, options, usage, 0); - if (argc != 1) - die("need a title for the progress output"); - title = argv[0]; + if (argc) + usage_with_options(usage, options); progress_testing = 1; - progress = start_progress(title, total); while (strbuf_getline(&line, stdin) != EOF) { char *end; - if (skip_prefix(line.buf, "progress ", (const char **) &end)) { + if (skip_prefix(line.buf, "start ", (const char **) &end)) { + uint64_t total = strtoull(end, &end, 10); + const char *title; + + /* + * We can't use "end + 1" as an argument to + * start_progress(), it doesn't xstrdup() its + * "title" argument. We need to hold onto a + * valid "char *" for it until the end. + */ + if (!*end) + title = default_title; + else if (*end == ' ') + title = string_list_insert(&titles, end + 1)->string; + else + die("invalid input: '%s'\n", line.buf); + + progress = start_progress(title, total); + } else if (skip_prefix(line.buf, "progress ", (const char **) &end)) { uint64_t item_count = strtoull(end, &end, 10); if (*end != '\0') die("invalid input: '%s'\n", line.buf); @@ -70,12 +83,16 @@ int cmd__progress(int argc, const char **argv) die("invalid input: '%s'\n", line.buf); progress_test_ns = test_ms * 1000 * 1000; display_throughput(progress, byte_count); - } else if (!strcmp(line.buf, "update")) + } else if (!strcmp(line.buf, "update")) { progress_test_force_update(); - else + } else if (!strcmp(line.buf, "stop")) { + stop_progress(&progress); + } else { die("invalid input: '%s'\n", line.buf); + } } - stop_progress(&progress); + strbuf_release(&line); + string_list_clear(&titles, 0); return 0; } diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c index a027217..2f65c7f 100644 --- a/t/helper/test-reach.c +++ b/t/helper/test-reach.c @@ -67,7 +67,7 @@ int cmd__reach(int ac, const char **av) die("failed to load commit for input %s resulting in oid %s\n", buf.buf, oid_to_hex(&oid)); - c = object_as_type(r, peeled, OBJ_COMMIT, 0); + c = object_as_type(peeled, OBJ_COMMIT, 0); if (!c) die("failed to load commit for input %s resulting in oid %s\n", @@ -107,8 +107,10 @@ int cmd__reach(int ac, const char **av) printf("%s(A,B):%d\n", av[1], ref_newer(&oid_A, &oid_B)); else if (!strcmp(av[1], "in_merge_bases")) printf("%s(A,B):%d\n", av[1], in_merge_bases(A, B)); + else if (!strcmp(av[1], "in_merge_bases_many")) + printf("%s(A,X):%d\n", av[1], in_merge_bases_many(A, X_nr, X_array)); else if (!strcmp(av[1], "is_descendant_of")) - printf("%s(A,X):%d\n", av[1], is_descendant_of(A, X)); + printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X)); else if (!strcmp(av[1], "get_merge_bases_many")) { struct commit_list *list = get_merge_bases_many(A, X_nr, X_array); printf("%s(A,X):\n", av[1]); @@ -164,5 +166,5 @@ int cmd__reach(int ac, const char **av) print_sorted_commit_ids(list); } - exit(0); + return 0; } diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c index 244977a..b736ef1 100644 --- a/t/helper/test-read-cache.c +++ b/t/helper/test-read-cache.c @@ -7,6 +7,8 @@ int cmd__read_cache(int argc, const char **argv) int i, cnt = 1; const char *name = NULL; + initialize_the_repository(); + if (argc > 1 && skip_prefix(argv[1], "--print-and-refresh=", &name)) { argc--; argv++; @@ -16,6 +18,7 @@ int cmd__read_cache(int argc, const char **argv) cnt = strtol(argv[1], NULL, 0); setup_git_directory(); git_config(git_default_config, NULL); + for (i = 0; i < cnt; i++) { read_cache(); if (name) { diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c index d2884ef..98b73bb 100644 --- a/t/helper/test-read-graph.c +++ b/t/helper/test-read-graph.c @@ -3,31 +3,22 @@ #include "commit-graph.h" #include "repository.h" #include "object-store.h" +#include "bloom.h" int cmd__read_graph(int argc, const char **argv) { struct commit_graph *graph = NULL; - char *graph_name; - int open_ok; - int fd; - struct stat st; - const char *object_dir; + struct object_directory *odb; setup_git_directory(); - object_dir = get_object_directory(); + odb = the_repository->objects->odb; - graph_name = get_commit_graph_filename(object_dir); + prepare_repo_settings(the_repository); - open_ok = open_commit_graph(graph_name, &fd, &st); - if (!open_ok) - die_errno(_("Could not open commit-graph '%s'"), graph_name); - - graph = load_commit_graph_one_fd_st(fd, &st); + graph = read_commit_graph_one(the_repository, odb); if (!graph) return 1; - FREE_AND_NULL(graph_name); - printf("header: %08x %d %d %d %d\n", ntohl(*(uint32_t*)graph->data), *(unsigned char*)(graph->data + 4), @@ -43,8 +34,28 @@ int cmd__read_graph(int argc, const char **argv) printf(" oid_lookup"); if (graph->chunk_commit_data) printf(" commit_metadata"); + if (graph->chunk_generation_data) + printf(" generation_data"); + if (graph->chunk_generation_data_overflow) + printf(" generation_data_overflow"); if (graph->chunk_extra_edges) printf(" extra_edges"); + if (graph->chunk_bloom_indexes) + printf(" bloom_indexes"); + if (graph->chunk_bloom_data) + printf(" bloom_data"); + printf("\n"); + + printf("options:"); + if (graph->bloom_filter_settings) + printf(" bloom(%"PRIu32",%"PRIu32",%"PRIu32")", + graph->bloom_filter_settings->hash_version, + graph->bloom_filter_settings->bits_per_entry, + graph->bloom_filter_settings->num_hashes); + if (graph->read_generation_data) + printf(" read_generation_data"); + if (graph->topo_levels) + printf(" topo_levels"); printf("\n"); UNLEAK(graph); diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c index 831b586..27072ba 100644 --- a/t/helper/test-read-midx.c +++ b/t/helper/test-read-midx.c @@ -3,18 +3,23 @@ #include "midx.h" #include "repository.h" #include "object-store.h" +#include "pack-bitmap.h" -static int read_midx_file(const char *object_dir) +static int read_midx_file(const char *object_dir, int show_objects) { uint32_t i; - struct multi_pack_index *m = load_multi_pack_index(object_dir, 1); + struct multi_pack_index *m; + + setup_git_directory(); + m = load_multi_pack_index(object_dir, 1); if (!m) return 1; - printf("header: %08x %d %d %d\n", + printf("header: %08x %d %d %d %d\n", m->signature, m->version, + m->hash_len, m->num_chunks, m->num_packs); @@ -39,13 +44,70 @@ static int read_midx_file(const char *object_dir) printf("object-dir: %s\n", m->object_dir); + if (show_objects) { + struct object_id oid; + struct pack_entry e; + + for (i = 0; i < m->num_objects; i++) { + nth_midxed_object_oid(&oid, m, i); + fill_midx_entry(the_repository, &oid, &e, m); + + printf("%s %"PRIu64"\t%s\n", + oid_to_hex(&oid), e.offset, e.p->pack_name); + } + } + + close_midx(m); + + return 0; +} + +static int read_midx_checksum(const char *object_dir) +{ + struct multi_pack_index *m; + + setup_git_directory(); + m = load_multi_pack_index(object_dir, 1); + if (!m) + return 1; + printf("%s\n", hash_to_hex(get_midx_checksum(m))); + return 0; +} + +static int read_midx_preferred_pack(const char *object_dir) +{ + struct multi_pack_index *midx = NULL; + struct bitmap_index *bitmap = NULL; + + setup_git_directory(); + + midx = load_multi_pack_index(object_dir, 1); + if (!midx) + return 1; + + bitmap = prepare_bitmap_git(the_repository); + if (!bitmap) + return 1; + if (!bitmap_is_midx(bitmap)) { + free_bitmap_index(bitmap); + return 1; + } + + printf("%s\n", midx->pack_names[midx_preferred_pack(bitmap)]); + free_bitmap_index(bitmap); return 0; } int cmd__read_midx(int argc, const char **argv) { - if (argc != 2) - usage("read-midx <object-dir>"); + if (!(argc == 2 || argc == 3)) + usage("read-midx [--show-objects|--checksum|--preferred-pack] <object-dir>"); - return read_midx_file(argv[1]); + if (!strcmp(argv[1], "--show-objects")) + return read_midx_file(argv[2], 1); + else if (!strcmp(argv[1], "--checksum")) + return read_midx_checksum(argv[2]); + else if (!strcmp(argv[1], "--preferred-pack")) + return read_midx_preferred_pack(argv[2]); + return read_midx_file(argv[1], 0); } diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index 799fc00..9646d85 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -5,6 +5,48 @@ #include "object-store.h" #include "repository.h" +struct flag_definition { + const char *name; + uint64_t mask; +}; + +#define FLAG_DEF(x) \ + { \ +#x, (x) \ + } + +static unsigned int parse_flags(const char *str, struct flag_definition *defs) +{ + struct string_list masks = STRING_LIST_INIT_DUP; + int i = 0; + unsigned int result = 0; + + if (!strcmp(str, "0")) + return 0; + + string_list_split(&masks, str, ',', 64); + for (; i < masks.nr; i++) { + const char *name = masks.items[i].string; + struct flag_definition *def = defs; + int found = 0; + while (def->name) { + if (!strcmp(def->name, name)) { + result |= def->mask; + found = 1; + break; + } + def++; + } + if (!found) + die("unknown flag \"%s\"", name); + } + + string_list_clear(&masks, 0); + return result; +} + +static struct flag_definition empty_flags[] = { { NULL, 0 } }; + static const char *notnull(const char *arg, const char *name) { if (!arg) @@ -12,9 +54,10 @@ static const char *notnull(const char *arg, const char *name) return arg; } -static unsigned int arg_flags(const char *arg, const char *name) +static unsigned int arg_flags(const char *arg, const char *name, + struct flag_definition *defs) { - return atoi(notnull(arg, name)); + return parse_flags(notnull(arg, name), defs); } static const char **get_store(const char **argv, struct ref_store **refs) @@ -37,7 +80,7 @@ static const char **get_store(const char **argv, struct ref_store **refs) *refs = get_submodule_ref_store(gitdir); } else if (skip_prefix(argv[0], "worktree:", &gitdir)) { - struct worktree **p, **worktrees = get_worktrees(0); + struct worktree **p, **worktrees = get_worktrees(); for (p = worktrees; *p; p++) { struct worktree *wt = *p; @@ -64,26 +107,17 @@ static const char **get_store(const char **argv, struct ref_store **refs) return argv + 1; } +static struct flag_definition pack_flags[] = { FLAG_DEF(PACK_REFS_PRUNE), + FLAG_DEF(PACK_REFS_ALL), + { NULL, 0 } }; static int cmd_pack_refs(struct ref_store *refs, const char **argv) { - unsigned int flags = arg_flags(*argv++, "flags"); + unsigned int flags = arg_flags(*argv++, "flags", pack_flags); return refs_pack_refs(refs, flags); } -static int cmd_peel_ref(struct ref_store *refs, const char **argv) -{ - const char *refname = notnull(*argv++, "refname"); - struct object_id oid; - int ret; - - ret = refs_peel_ref(refs, refname, &oid); - if (!ret) - puts(oid_to_hex(&oid)); - return ret; -} - static int cmd_create_symref(struct ref_store *refs, const char **argv) { const char *refname = notnull(*argv++, "refname"); @@ -93,16 +127,27 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv) return refs_create_symref(refs, refname, target, logmsg); } +static struct flag_definition transaction_flags[] = { + FLAG_DEF(REF_NO_DEREF), + FLAG_DEF(REF_FORCE_CREATE_REFLOG), + FLAG_DEF(REF_SKIP_OID_VERIFICATION), + FLAG_DEF(REF_SKIP_REFNAME_VERIFICATION), + { NULL, 0 } +}; + static int cmd_delete_refs(struct ref_store *refs, const char **argv) { - unsigned int flags = arg_flags(*argv++, "flags"); + unsigned int flags = arg_flags(*argv++, "flags", transaction_flags); const char *msg = *argv++; struct string_list refnames = STRING_LIST_INIT_NODUP; + int result; while (*argv) string_list_append(&refnames, *argv++); - return refs_delete_refs(refs, msg, &refnames, flags); + result = refs_delete_refs(refs, msg, &refnames, flags); + string_list_clear(&refnames, 0); + return result; } static int cmd_rename_ref(struct ref_store *refs, const char **argv) @@ -130,9 +175,9 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv) static int cmd_resolve_ref(struct ref_store *refs, const char **argv) { - struct object_id oid; + struct object_id oid = *null_oid(); const char *refname = notnull(*argv++, "refname"); - int resolve_flags = arg_flags(*argv++, "resolve-flags"); + int resolve_flags = arg_flags(*argv++, "resolve-flags", empty_flags); int flags; const char *ref; @@ -163,9 +208,9 @@ static int each_reflog(struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { - printf("%s %s %s %"PRItime" %d %s\n", - oid_to_hex(old_oid), oid_to_hex(new_oid), - committer, timestamp, tz, msg); + printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid), + oid_to_hex(new_oid), committer, timestamp, tz, + *msg == '\n' ? "" : "\t", msg); return 0; } @@ -193,11 +238,10 @@ static int cmd_reflog_exists(struct ref_store *refs, const char **argv) static int cmd_create_reflog(struct ref_store *refs, const char **argv) { const char *refname = notnull(*argv++, "refname"); - int force_create = arg_flags(*argv++, "force-create"); struct strbuf err = STRBUF_INIT; int ret; - ret = refs_create_reflog(refs, refname, force_create, &err); + ret = refs_create_reflog(refs, refname, &err); if (err.len) puts(err.buf); return ret; @@ -220,11 +264,11 @@ static int cmd_delete_ref(struct ref_store *refs, const char **argv) const char *msg = notnull(*argv++, "msg"); const char *refname = notnull(*argv++, "refname"); const char *sha1_buf = notnull(*argv++, "old-sha1"); - unsigned int flags = arg_flags(*argv++, "flags"); + unsigned int flags = arg_flags(*argv++, "flags", transaction_flags); struct object_id old_oid; if (get_oid_hex(sha1_buf, &old_oid)) - die("not sha-1"); + die("cannot parse %s as %s", sha1_buf, the_hash_algo->name); return refs_delete_ref(refs, msg, refname, &old_oid, flags); } @@ -235,13 +279,14 @@ static int cmd_update_ref(struct ref_store *refs, const char **argv) const char *refname = notnull(*argv++, "refname"); const char *new_sha1_buf = notnull(*argv++, "new-sha1"); const char *old_sha1_buf = notnull(*argv++, "old-sha1"); - unsigned int flags = arg_flags(*argv++, "flags"); + unsigned int flags = arg_flags(*argv++, "flags", transaction_flags); struct object_id old_oid; struct object_id new_oid; - if (get_oid_hex(old_sha1_buf, &old_oid) || - get_oid_hex(new_sha1_buf, &new_oid)) - die("not sha-1"); + if (get_oid_hex(old_sha1_buf, &old_oid)) + die("cannot parse %s as %s", old_sha1_buf, the_hash_algo->name); + if (get_oid_hex(new_sha1_buf, &new_oid)) + die("cannot parse %s as %s", new_sha1_buf, the_hash_algo->name); return refs_update_ref(refs, msg, refname, &new_oid, &old_oid, @@ -255,7 +300,6 @@ struct command { static struct command commands[] = { { "pack-refs", cmd_pack_refs }, - { "peel-ref", cmd_peel_ref }, { "create-symref", cmd_create_symref }, { "delete-refs", cmd_delete_refs }, { "rename-ref", cmd_rename_ref }, diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c new file mode 100644 index 0000000..1f0a28c --- /dev/null +++ b/t/helper/test-reftable.c @@ -0,0 +1,22 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable(int argc, const char **argv) +{ + /* test from simple to complex. */ + basics_test_main(argc, argv); + record_test_main(argc, argv); + block_test_main(argc, argv); + tree_test_main(argc, argv); + pq_test_main(argc, argv); + readwrite_test_main(argc, argv); + merged_test_main(argc, argv); + stack_test_main(argc, argv); + refname_test_main(argc, argv); + return 0; +} + +int cmd__dump_reftable(int argc, const char **argv) +{ + return reftable_dump_main(argc, (char *const *)argv); +} diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c index 10284cc..d6f28ca 100644 --- a/t/helper/test-regex.c +++ b/t/helper/test-regex.c @@ -1,5 +1,4 @@ #include "test-tool.h" -#include "git-compat-util.h" #include "gettext.h" struct reg_flag { @@ -8,12 +7,13 @@ struct reg_flag { }; static struct reg_flag reg_flags[] = { - { "EXTENDED", REG_EXTENDED }, - { "NEWLINE", REG_NEWLINE }, - { "ICASE", REG_ICASE }, - { "NOTBOL", REG_NOTBOL }, + { "EXTENDED", REG_EXTENDED }, + { "NEWLINE", REG_NEWLINE }, + { "ICASE", REG_ICASE }, + { "NOTBOL", REG_NOTBOL }, + { "NOTEOL", REG_NOTEOL }, #ifdef REG_STARTEND - { "STARTEND", REG_STARTEND }, + { "STARTEND", REG_STARTEND }, #endif { NULL, 0 } }; @@ -41,36 +41,74 @@ int cmd__regex(int argc, const char **argv) { const char *pat; const char *str; - int flags = 0; + int ret, silent = 0, flags = 0; regex_t r; regmatch_t m[1]; - - if (argc == 2 && !strcmp(argv[1], "--bug")) - return test_regex_bug(); - else if (argc < 3) - usage("test-tool regex --bug\n" - "test-tool regex <pattern> <string> [<options>]"); + char errbuf[64]; argv++; - pat = *argv++; - str = *argv++; - while (*argv) { - struct reg_flag *rf; - for (rf = reg_flags; rf->name; rf++) - if (!strcmp(*argv, rf->name)) { - flags |= rf->flag; - break; - } - if (!rf->name) - die("do not recognize %s", *argv); + argc--; + + if (!argc) + goto usage; + + if (!strcmp(*argv, "--bug")) { + if (argc == 1) + return test_regex_bug(); + else + goto usage; + } + if (!strcmp(*argv, "--silent")) { + silent = 1; argv++; + argc--; + } + if (!argc) + goto usage; + + pat = *argv++; + if (argc == 1) + str = NULL; + else { + str = *argv++; + while (*argv) { + struct reg_flag *rf; + for (rf = reg_flags; rf->name; rf++) + if (!strcmp(*argv, rf->name)) { + flags |= rf->flag; + break; + } + if (!rf->name) + die("do not recognize flag %s", *argv); + argv++; + } } git_setup_gettext(); - if (regcomp(&r, pat, flags)) - die("failed regcomp() for pattern '%s'", pat); - if (regexec(&r, str, 1, m, 0)) - return 1; + ret = regcomp(&r, pat, flags); + if (ret) { + if (silent) + return ret; + + regerror(ret, &r, errbuf, sizeof(errbuf)); + die("failed regcomp() for pattern '%s' (%s)", pat, errbuf); + } + if (!str) + return 0; + + ret = regexec(&r, str, 1, m, 0); + if (ret) { + if (silent || ret == REG_NOMATCH) + return ret; + + regerror(ret, &r, errbuf, sizeof(errbuf)); + die("failed regexec() for subject '%s' (%s)", str, errbuf); + } return 0; +usage: + usage("\ttest-tool regex --bug\n" + "\ttest-tool regex [--silent] <pattern>\n" + "\ttest-tool regex [--silent] <pattern> <string> [<options>]"); + return -1; } diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index f7f8618..56f0e3c 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -19,12 +19,11 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, memset(the_repository, 0, sizeof(*the_repository)); - /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */ - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); - if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); + repo_set_hash_algo(the_repository, hash_algo_by_ptr(r.hash_algo)); + c = lookup_commit(&r, commit_oid); if (!parse_commit_in_graph(&r, c)) @@ -50,12 +49,11 @@ static void test_get_commit_tree_in_graph(const char *gitdir, memset(the_repository, 0, sizeof(*the_repository)); - /* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */ - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); - if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); + repo_set_hash_algo(the_repository, hash_algo_by_ptr(r.hash_algo)); + c = lookup_commit(&r, commit_oid); /* @@ -75,6 +73,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir, int cmd__repository(int argc, const char **argv) { + int nongit_ok = 0; + + setup_git_directory_gently(&nongit_ok); + if (argc < 2) die("must have at least 2 arguments"); if (!strcmp(argv[1], "parse_commit_in_graph")) { diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c index 625b2db..4a45d5b 100644 --- a/t/helper/test-revision-walking.c +++ b/t/helper/test-revision-walking.c @@ -43,6 +43,7 @@ static int run_revision_walk(void) } reset_revision_walk(); + release_revisions(&rev); return got_revision; } diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c index 1646aa2..c9283b4 100644 --- a/t/helper/test-run-command.c +++ b/t/helper/test-run-command.c @@ -12,14 +12,13 @@ #include "git-compat-util.h" #include "cache.h" #include "run-command.h" -#include "argv-array.h" +#include "strvec.h" #include "strbuf.h" #include "parse-options.h" #include "string-list.h" #include "thread-utils.h" #include "wildmatch.h" #include "gettext.h" -#include "parse-options.h" static int number_callbacks; static int parallel_next(struct child_process *cp, @@ -31,8 +30,12 @@ static int parallel_next(struct child_process *cp, if (number_callbacks >= 4) return 0; - argv_array_pushv(&cp->args, d->argv); - strbuf_addstr(err, "preloaded output of a child\n"); + strvec_pushv(&cp->args, d->args.v); + if (err) + strbuf_addstr(err, "preloaded output of a child\n"); + else + fprintf(stderr, "preloaded output of a child\n"); + number_callbacks++; return 1; } @@ -42,7 +45,10 @@ static int no_job(struct child_process *cp, void *cb, void **task_cb) { - strbuf_addstr(err, "no further jobs available\n"); + if (err) + strbuf_addstr(err, "no further jobs available\n"); + else + fprintf(stderr, "no further jobs available\n"); return 0; } @@ -51,7 +57,10 @@ static int task_finished(int result, void *pp_cb, void *pp_task_cb) { - strbuf_addstr(err, "asking for a quick stop\n"); + if (err) + strbuf_addstr(err, "asking for a quick stop\n"); + else + fprintf(stderr, "asking for a quick stop\n"); return 1; } @@ -60,8 +69,10 @@ struct testsuite { int next; int quiet, immediate, verbose, verbose_log, trace, write_junit_xml; }; -#define TESTSUITE_INIT \ - { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, -1, 0, 0, 0, 0, 0, 0 } +#define TESTSUITE_INIT { \ + .tests = STRING_LIST_INIT_DUP, \ + .failed = STRING_LIST_INIT_DUP, \ +} static int next_test(struct child_process *cp, struct strbuf *err, void *cb, void **task_cb) @@ -72,19 +83,19 @@ static int next_test(struct child_process *cp, struct strbuf *err, void *cb, return 0; test = suite->tests.items[suite->next++].string; - argv_array_pushl(&cp->args, "sh", test, NULL); + strvec_pushl(&cp->args, "sh", test, NULL); if (suite->quiet) - argv_array_push(&cp->args, "--quiet"); + strvec_push(&cp->args, "--quiet"); if (suite->immediate) - argv_array_push(&cp->args, "-i"); + strvec_push(&cp->args, "-i"); if (suite->verbose) - argv_array_push(&cp->args, "-v"); + strvec_push(&cp->args, "-v"); if (suite->verbose_log) - argv_array_push(&cp->args, "-V"); + strvec_push(&cp->args, "-V"); if (suite->trace) - argv_array_push(&cp->args, "-x"); + strvec_push(&cp->args, "-x"); if (suite->write_junit_xml) - argv_array_push(&cp->args, "--write-junit-xml"); + strvec_push(&cp->args, "--write-junit-xml"); strbuf_addf(err, "Output of '%s':\n", test); *task_cb = (void *)test; @@ -142,9 +153,6 @@ static int testsuite(int argc, const char **argv) OPT_END() }; - memset(&suite, 0, sizeof(suite)); - suite.tests.strdup_strings = suite.failed.strdup_strings = 1; - argc = parse_options(argc, argv, NULL, options, testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -181,15 +189,16 @@ static int testsuite(int argc, const char **argv) if (max_jobs > suite.tests.nr) max_jobs = suite.tests.nr; - fprintf(stderr, "Running %d tests (%d at a time)\n", - suite.tests.nr, max_jobs); + fprintf(stderr, "Running %"PRIuMAX" tests (%d at a time)\n", + (uintmax_t)suite.tests.nr, max_jobs); ret = run_processes_parallel(max_jobs, next_test, test_failed, test_finished, &suite); if (suite.failed.nr > 0) { ret = 1; - fprintf(stderr, "%d tests failed:\n\n", suite.failed.nr); + fprintf(stderr, "%"PRIuMAX" tests failed:\n\n", + (uintmax_t)suite.failed.nr); for (i = 0; i < suite.failed.nr; i++) fprintf(stderr, "\t%s\n", suite.failed.items[i].string); } @@ -220,11 +229,11 @@ static int quote_stress_test(int argc, const char **argv) char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a"; int i, j, k, trials = 100, skip = 0, msys2 = 0; struct strbuf out = STRBUF_INIT; - struct argv_array args = ARGV_ARRAY_INIT; + struct strvec args = STRVEC_INIT; struct option options[] = { - OPT_INTEGER('n', "trials", &trials, "Number of trials"), - OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"), - OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"), + OPT_INTEGER('n', "trials", &trials, "number of trials"), + OPT_INTEGER('s', "skip", &skip, "skip <n> trials"), + OPT_BOOL('m', "msys2", &msys2, "test quoting for MSYS2's sh"), OPT_END() }; const char * const usage[] = { @@ -241,20 +250,20 @@ static int quote_stress_test(int argc, const char **argv) size_t arg_count, arg_offset; int ret = 0; - argv_array_clear(&args); + strvec_clear(&args); if (msys2) - argv_array_pushl(&args, "sh", "-c", - "printf %s\\\\0 \"$@\"", "skip", NULL); + strvec_pushl(&args, "sh", "-c", + "printf %s\\\\0 \"$@\"", "skip", NULL); else - argv_array_pushl(&args, "test-tool", "run-command", - "quote-echo", NULL); - arg_offset = args.argc; + strvec_pushl(&args, "test-tool", "run-command", + "quote-echo", NULL); + arg_offset = args.nr; if (argc > 0) { trials = 1; arg_count = argc; for (j = 0; j < arg_count; j++) - argv_array_push(&args, argv[j]); + strvec_push(&args, argv[j]); } else { arg_count = 1 + (my_random() % 5); for (j = 0; j < arg_count; j++) { @@ -268,20 +277,20 @@ static int quote_stress_test(int argc, const char **argv) ARRAY_SIZE(special)]; buf[arg_len] = '\0'; - argv_array_push(&args, buf); + strvec_push(&args, buf); } } if (i < skip) continue; - cp.argv = args.argv; + strvec_pushv(&cp.args, args.v); strbuf_reset(&out); if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0) return error("Failed to spawn child process"); for (j = 0, k = 0; j < arg_count; j++) { - const char *arg = args.argv[j + arg_offset]; + const char *arg = args.v[j + arg_offset]; if (strcmp(arg, out.buf + k)) ret = error("incorrectly quoted arg: '%s', " @@ -298,10 +307,10 @@ static int quote_stress_test(int argc, const char **argv) fprintf(stderr, "Trial #%d failed. Arguments:\n", i); for (j = 0; j < arg_count; j++) fprintf(stderr, "arg #%d: '%s'\n", - (int)j, args.argv[j + arg_offset]); + (int)j, args.v[j + arg_offset]); strbuf_release(&out); - argv_array_clear(&args); + strvec_clear(&args); return ret; } @@ -311,7 +320,7 @@ static int quote_stress_test(int argc, const char **argv) } strbuf_release(&out); - argv_array_clear(&args); + strvec_clear(&args); return 0; } @@ -338,8 +347,8 @@ static int inherit_handle(const char *argv0) xsnprintf(path, sizeof(path), "out-XXXXXX"); tmp = xmkstemp(path); - argv_array_pushl(&cp.args, - "test-tool", argv0, "inherited-handle-child", NULL); + strvec_pushl(&cp.args, + "test-tool", argv0, "inherited-handle-child", NULL); cp.in = -1; cp.no_stdout = cp.no_stderr = 1; if (start_command(&cp) < 0) @@ -391,13 +400,13 @@ int cmd__run_command(int argc, const char **argv) while (!strcmp(argv[1], "env")) { if (!argv[2]) die("env specifier without a value"); - argv_array_push(&proc.env_array, argv[2]); + strvec_push(&proc.env, argv[2]); argv += 2; argc -= 2; } if (argc < 3) return 1; - proc.argv = (const char **)argv + 2; + strvec_pushv(&proc.args, (const char **)argv + 2); if (!strcmp(argv[1], "start-command-ENOENT")) { if (start_command(&proc) < 0 && errno == ENOENT) @@ -408,8 +417,15 @@ int cmd__run_command(int argc, const char **argv) if (!strcmp(argv[1], "run-command")) exit(run_command(&proc)); + if (!strcmp(argv[1], "--ungroup")) { + argv += 1; + argc -= 1; + run_processes_parallel_ungroup = 1; + } + jobs = atoi(argv[2]); - proc.argv = (const char **)argv + 3; + strvec_clear(&proc.args); + strvec_pushv(&proc.args, (const char **)argv + 3); if (!strcmp(argv[1], "run-command-parallel")) exit(run_processes_parallel(jobs, parallel_next, diff --git a/t/helper/test-serve-v2.c b/t/helper/test-serve-v2.c index aee35e5..28e905a 100644 --- a/t/helper/test-serve-v2.c +++ b/t/helper/test-serve-v2.c @@ -10,12 +10,12 @@ static char const * const serve_usage[] = { int cmd__serve_v2(int argc, const char **argv) { - struct serve_options opts = SERVE_OPTIONS_INIT; - + int stateless_rpc = 0; + int advertise_capabilities = 0; struct option options[] = { - OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc, + OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("quit after a single request/response exchange")), - OPT_BOOL(0, "advertise-capabilities", &opts.advertise_capabilities, + OPT_BOOL(0, "advertise-capabilities", &advertise_capabilities, N_("exit immediately after advertising capabilities")), OPT_END() }; @@ -25,7 +25,11 @@ int cmd__serve_v2(int argc, const char **argv) argc = parse_options(argc, argv, prefix, options, serve_usage, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN); - serve(&opts); + + if (advertise_capabilities) + protocol_v2_advertise_capabilities(); + else + protocol_v2_serve_loop(stateless_rpc); return 0; } diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c new file mode 100644 index 0000000..28365ff --- /dev/null +++ b/t/helper/test-simple-ipc.c @@ -0,0 +1,686 @@ +/* + * test-simple-ipc.c: verify that the Inter-Process Communication works. + */ + +#include "test-tool.h" +#include "cache.h" +#include "strbuf.h" +#include "simple-ipc.h" +#include "parse-options.h" +#include "thread-utils.h" +#include "strvec.h" +#include "run-command.h" + +#ifndef SUPPORTS_SIMPLE_IPC +int cmd__simple_ipc(int argc, const char **argv) +{ + die("simple IPC not available on this platform"); +} +#else + +/* + * The test daemon defines an "application callback" that supports a + * series of commands (see `test_app_cb()`). + * + * Unknown commands are caught here and we send an error message back + * to the client process. + */ +static int app__unhandled_command(const char *command, + ipc_server_reply_cb *reply_cb, + struct ipc_server_reply_data *reply_data) +{ + struct strbuf buf = STRBUF_INIT; + int ret; + + strbuf_addf(&buf, "unhandled command: %s", command); + ret = reply_cb(reply_data, buf.buf, buf.len); + strbuf_release(&buf); + + return ret; +} + +/* + * Reply with a single very large buffer. This is to ensure that + * long response are properly handled -- whether the chunking occurs + * in the kernel or in the (probably pkt-line) layer. + */ +#define BIG_ROWS (10000) +static int app__big_command(ipc_server_reply_cb *reply_cb, + struct ipc_server_reply_data *reply_data) +{ + struct strbuf buf = STRBUF_INIT; + int row; + int ret; + + for (row = 0; row < BIG_ROWS; row++) + strbuf_addf(&buf, "big: %.75d\n", row); + + ret = reply_cb(reply_data, buf.buf, buf.len); + strbuf_release(&buf); + + return ret; +} + +/* + * Reply with a series of lines. This is to ensure that we can incrementally + * compute the response and chunk it to the client. + */ +#define CHUNK_ROWS (10000) +static int app__chunk_command(ipc_server_reply_cb *reply_cb, + struct ipc_server_reply_data *reply_data) +{ + struct strbuf buf = STRBUF_INIT; + int row; + int ret; + + for (row = 0; row < CHUNK_ROWS; row++) { + strbuf_setlen(&buf, 0); + strbuf_addf(&buf, "big: %.75d\n", row); + ret = reply_cb(reply_data, buf.buf, buf.len); + } + + strbuf_release(&buf); + + return ret; +} + +/* + * Slowly reply with a series of lines. This is to model an expensive to + * compute chunked response (which might happen if this callback is running + * in a thread and is fighting for a lock with other threads). + */ +#define SLOW_ROWS (1000) +#define SLOW_DELAY_MS (10) +static int app__slow_command(ipc_server_reply_cb *reply_cb, + struct ipc_server_reply_data *reply_data) +{ + struct strbuf buf = STRBUF_INIT; + int row; + int ret; + + for (row = 0; row < SLOW_ROWS; row++) { + strbuf_setlen(&buf, 0); + strbuf_addf(&buf, "big: %.75d\n", row); + ret = reply_cb(reply_data, buf.buf, buf.len); + sleep_millisec(SLOW_DELAY_MS); + } + + strbuf_release(&buf); + + return ret; +} + +/* + * The client sent a command followed by a (possibly very) large buffer. + */ +static int app__sendbytes_command(const char *received, size_t received_len, + ipc_server_reply_cb *reply_cb, + struct ipc_server_reply_data *reply_data) +{ + struct strbuf buf_resp = STRBUF_INIT; + const char *p = "?"; + int len_ballast = 0; + int k; + int errs = 0; + int ret; + + /* + * The test is setup to send: + * "sendbytes" SP <n * char> + */ + if (received_len < strlen("sendbytes ")) + BUG("received_len is short in app__sendbytes_command"); + + if (skip_prefix(received, "sendbytes ", &p)) + len_ballast = strlen(p); + + /* + * Verify that the ballast is n copies of a single letter. + * And that the multi-threaded IO layer didn't cross the streams. + */ + for (k = 1; k < len_ballast; k++) + if (p[k] != p[0]) + errs++; + + if (errs) + strbuf_addf(&buf_resp, "errs:%d\n", errs); + else + strbuf_addf(&buf_resp, "rcvd:%c%08d\n", p[0], len_ballast); + + ret = reply_cb(reply_data, buf_resp.buf, buf_resp.len); + + strbuf_release(&buf_resp); + + return ret; +} + +/* + * An arbitrary fixed address to verify that the application instance + * data is handled properly. + */ +static int my_app_data = 42; + +static ipc_server_application_cb test_app_cb; + +/* + * This is the "application callback" that sits on top of the + * "ipc-server". It completely defines the set of commands supported + * by this application. + */ +static int test_app_cb(void *application_data, + const char *command, size_t command_len, + ipc_server_reply_cb *reply_cb, + struct ipc_server_reply_data *reply_data) +{ + /* + * Verify that we received the application-data that we passed + * when we started the ipc-server. (We have several layers of + * callbacks calling callbacks and it's easy to get things mixed + * up (especially when some are "void*").) + */ + if (application_data != (void*)&my_app_data) + BUG("application_cb: application_data pointer wrong"); + + if (command_len == 4 && !strncmp(command, "quit", 4)) { + /* + * The client sent a "quit" command. This is an async + * request for the server to shutdown. + * + * We DO NOT send the client a response message + * (because we have nothing to say and the other + * server threads have not yet stopped). + * + * Tell the ipc-server layer to start shutting down. + * This includes: stop listening for new connections + * on the socket/pipe and telling all worker threads + * to finish/drain their outgoing responses to other + * clients. + * + * This DOES NOT force an immediate sync shutdown. + */ + return SIMPLE_IPC_QUIT; + } + + if (command_len == 4 && !strncmp(command, "ping", 4)) { + const char *answer = "pong"; + return reply_cb(reply_data, answer, strlen(answer)); + } + + if (command_len == 3 && !strncmp(command, "big", 3)) + return app__big_command(reply_cb, reply_data); + + if (command_len == 5 && !strncmp(command, "chunk", 5)) + return app__chunk_command(reply_cb, reply_data); + + if (command_len == 4 && !strncmp(command, "slow", 4)) + return app__slow_command(reply_cb, reply_data); + + if (command_len >= 10 && starts_with(command, "sendbytes ")) + return app__sendbytes_command(command, command_len, + reply_cb, reply_data); + + return app__unhandled_command(command, reply_cb, reply_data); +} + +struct cl_args +{ + const char *subcommand; + const char *path; + const char *token; + + int nr_threads; + int max_wait_sec; + int bytecount; + int batchsize; + + char bytevalue; +}; + +static struct cl_args cl_args = { + .subcommand = NULL, + .path = "ipc-test", + .token = NULL, + + .nr_threads = 5, + .max_wait_sec = 60, + .bytecount = 1024, + .batchsize = 10, + + .bytevalue = 'x', +}; + +/* + * This process will run as a simple-ipc server and listen for IPC commands + * from client processes. + */ +static int daemon__run_server(void) +{ + int ret; + + struct ipc_server_opts opts = { + .nr_threads = cl_args.nr_threads, + }; + + /* + * Synchronously run the ipc-server. We don't need any application + * instance data, so pass an arbitrary pointer (that we'll later + * verify made the round trip). + */ + ret = ipc_server_run(cl_args.path, &opts, test_app_cb, (void*)&my_app_data); + if (ret == -2) + error("socket/pipe already in use: '%s'", cl_args.path); + else if (ret == -1) + error_errno("could not start server on: '%s'", cl_args.path); + + return ret; +} + +static start_bg_wait_cb bg_wait_cb; + +static int bg_wait_cb(const struct child_process *cp, void *cb_data) +{ + int s = ipc_get_active_state(cl_args.path); + + switch (s) { + case IPC_STATE__LISTENING: + /* child is "ready" */ + return 0; + + case IPC_STATE__NOT_LISTENING: + case IPC_STATE__PATH_NOT_FOUND: + /* give child more time */ + return 1; + + default: + case IPC_STATE__INVALID_PATH: + case IPC_STATE__OTHER_ERROR: + /* all the time in world won't help */ + return -1; + } +} + +static int daemon__start_server(void) +{ + struct child_process cp = CHILD_PROCESS_INIT; + enum start_bg_result sbgr; + + strvec_push(&cp.args, "test-tool"); + strvec_push(&cp.args, "simple-ipc"); + strvec_push(&cp.args, "run-daemon"); + strvec_pushf(&cp.args, "--name=%s", cl_args.path); + strvec_pushf(&cp.args, "--threads=%d", cl_args.nr_threads); + + cp.no_stdin = 1; + cp.no_stdout = 1; + cp.no_stderr = 1; + + sbgr = start_bg_command(&cp, bg_wait_cb, NULL, cl_args.max_wait_sec); + + switch (sbgr) { + case SBGR_READY: + return 0; + + default: + case SBGR_ERROR: + case SBGR_CB_ERROR: + return error("daemon failed to start"); + + case SBGR_TIMEOUT: + return error("daemon not online yet"); + + case SBGR_DIED: + return error("daemon terminated"); + } +} + +/* + * This process will run a quick probe to see if a simple-ipc server + * is active on this path. + * + * Returns 0 if the server is alive. + */ +static int client__probe_server(void) +{ + enum ipc_active_state s; + + s = ipc_get_active_state(cl_args.path); + switch (s) { + case IPC_STATE__LISTENING: + return 0; + + case IPC_STATE__NOT_LISTENING: + return error("no server listening at '%s'", cl_args.path); + + case IPC_STATE__PATH_NOT_FOUND: + return error("path not found '%s'", cl_args.path); + + case IPC_STATE__INVALID_PATH: + return error("invalid pipe/socket name '%s'", cl_args.path); + + case IPC_STATE__OTHER_ERROR: + default: + return error("other error for '%s'", cl_args.path); + } +} + +/* + * Send an IPC command token to an already-running server daemon and + * print the response. + * + * This is a simple 1 word command/token that `test_app_cb()` (in the + * daemon process) will understand. + */ +static int client__send_ipc(void) +{ + const char *command = "(no-command)"; + struct strbuf buf = STRBUF_INIT; + struct ipc_client_connect_options options + = IPC_CLIENT_CONNECT_OPTIONS_INIT; + + if (cl_args.token && *cl_args.token) + command = cl_args.token; + + options.wait_if_busy = 1; + options.wait_if_not_found = 0; + + if (!ipc_client_send_command(cl_args.path, &options, + command, strlen(command), + &buf)) { + if (buf.len) { + printf("%s\n", buf.buf); + fflush(stdout); + } + strbuf_release(&buf); + + return 0; + } + + return error("failed to send '%s' to '%s'", command, cl_args.path); +} + +/* + * Send an IPC command to an already-running server and ask it to + * shutdown. "send quit" is an async request and queues a shutdown + * event in the server, so we spin and wait here for it to actually + * shutdown to make the unit tests a little easier to write. + */ +static int client__stop_server(void) +{ + int ret; + time_t time_limit, now; + enum ipc_active_state s; + + time(&time_limit); + time_limit += cl_args.max_wait_sec; + + cl_args.token = "quit"; + + ret = client__send_ipc(); + if (ret) + return ret; + + for (;;) { + sleep_millisec(100); + + s = ipc_get_active_state(cl_args.path); + + if (s != IPC_STATE__LISTENING) { + /* + * The socket/pipe is gone and/or has stopped + * responding. Lets assume that the daemon + * process has exited too. + */ + return 0; + } + + time(&now); + if (now > time_limit) + return error("daemon has not shutdown yet"); + } +} + +/* + * Send an IPC command followed by ballast to confirm that a large + * message can be sent and that the kernel or pkt-line layers will + * properly chunk it and that the daemon receives the entire message. + */ +static int do_sendbytes(int bytecount, char byte, const char *path, + const struct ipc_client_connect_options *options) +{ + struct strbuf buf_send = STRBUF_INIT; + struct strbuf buf_resp = STRBUF_INIT; + + strbuf_addstr(&buf_send, "sendbytes "); + strbuf_addchars(&buf_send, byte, bytecount); + + if (!ipc_client_send_command(path, options, + buf_send.buf, buf_send.len, + &buf_resp)) { + strbuf_rtrim(&buf_resp); + printf("sent:%c%08d %s\n", byte, bytecount, buf_resp.buf); + fflush(stdout); + strbuf_release(&buf_send); + strbuf_release(&buf_resp); + + return 0; + } + + return error("client failed to sendbytes(%d, '%c') to '%s'", + bytecount, byte, path); +} + +/* + * Send an IPC command with ballast to an already-running server daemon. + */ +static int client__sendbytes(void) +{ + struct ipc_client_connect_options options + = IPC_CLIENT_CONNECT_OPTIONS_INIT; + + options.wait_if_busy = 1; + options.wait_if_not_found = 0; + options.uds_disallow_chdir = 0; + + return do_sendbytes(cl_args.bytecount, cl_args.bytevalue, cl_args.path, + &options); +} + +struct multiple_thread_data { + pthread_t pthread_id; + struct multiple_thread_data *next; + const char *path; + int bytecount; + int batchsize; + int sum_errors; + int sum_good; + char letter; +}; + +static void *multiple_thread_proc(void *_multiple_thread_data) +{ + struct multiple_thread_data *d = _multiple_thread_data; + int k; + struct ipc_client_connect_options options + = IPC_CLIENT_CONNECT_OPTIONS_INIT; + + options.wait_if_busy = 1; + options.wait_if_not_found = 0; + /* + * A multi-threaded client should not be randomly calling chdir(). + * The test will pass without this restriction because the test is + * not otherwise accessing the filesystem, but it makes us honest. + */ + options.uds_disallow_chdir = 1; + + trace2_thread_start("multiple"); + + for (k = 0; k < d->batchsize; k++) { + if (do_sendbytes(d->bytecount + k, d->letter, d->path, &options)) + d->sum_errors++; + else + d->sum_good++; + } + + trace2_thread_exit(); + return NULL; +} + +/* + * Start a client-side thread pool. Each thread sends a series of + * IPC requests. Each request is on a new connection to the server. + */ +static int client__multiple(void) +{ + struct multiple_thread_data *list = NULL; + int k; + int sum_join_errors = 0; + int sum_thread_errors = 0; + int sum_good = 0; + + for (k = 0; k < cl_args.nr_threads; k++) { + struct multiple_thread_data *d = xcalloc(1, sizeof(*d)); + d->next = list; + d->path = cl_args.path; + d->bytecount = cl_args.bytecount + cl_args.batchsize*(k/26); + d->batchsize = cl_args.batchsize; + d->sum_errors = 0; + d->sum_good = 0; + d->letter = 'A' + (k % 26); + + if (pthread_create(&d->pthread_id, NULL, multiple_thread_proc, d)) { + warning("failed to create thread[%d] skipping remainder", k); + free(d); + break; + } + + list = d; + } + + while (list) { + struct multiple_thread_data *d = list; + + if (pthread_join(d->pthread_id, NULL)) + sum_join_errors++; + + sum_thread_errors += d->sum_errors; + sum_good += d->sum_good; + + list = d->next; + free(d); + } + + printf("client (good %d) (join %d), (errors %d)\n", + sum_good, sum_join_errors, sum_thread_errors); + + return (sum_join_errors + sum_thread_errors) ? 1 : 0; +} + +int cmd__simple_ipc(int argc, const char **argv) +{ + const char * const simple_ipc_usage[] = { + N_("test-helper simple-ipc is-active [<name>] [<options>]"), + N_("test-helper simple-ipc run-daemon [<name>] [<threads>]"), + N_("test-helper simple-ipc start-daemon [<name>] [<threads>] [<max-wait>]"), + N_("test-helper simple-ipc stop-daemon [<name>] [<max-wait>]"), + N_("test-helper simple-ipc send [<name>] [<token>]"), + N_("test-helper simple-ipc sendbytes [<name>] [<bytecount>] [<byte>]"), + N_("test-helper simple-ipc multiple [<name>] [<threads>] [<bytecount>] [<batchsize>]"), + NULL + }; + + const char *bytevalue = NULL; + + struct option options[] = { +#ifndef GIT_WINDOWS_NATIVE + OPT_STRING(0, "name", &cl_args.path, N_("name"), N_("name or pathname of unix domain socket")), +#else + OPT_STRING(0, "name", &cl_args.path, N_("name"), N_("named-pipe name")), +#endif + OPT_INTEGER(0, "threads", &cl_args.nr_threads, N_("number of threads in server thread pool")), + OPT_INTEGER(0, "max-wait", &cl_args.max_wait_sec, N_("seconds to wait for daemon to start or stop")), + + OPT_INTEGER(0, "bytecount", &cl_args.bytecount, N_("number of bytes")), + OPT_INTEGER(0, "batchsize", &cl_args.batchsize, N_("number of requests per thread")), + + OPT_STRING(0, "byte", &bytevalue, N_("byte"), N_("ballast character")), + OPT_STRING(0, "token", &cl_args.token, N_("token"), N_("command token to send to the server")), + + OPT_END() + }; + + if (argc < 2) + usage_with_options(simple_ipc_usage, options); + + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(simple_ipc_usage, options); + + if (argc == 2 && !strcmp(argv[1], "SUPPORTS_SIMPLE_IPC")) + return 0; + + cl_args.subcommand = argv[1]; + + argc--; + argv++; + + argc = parse_options(argc, argv, NULL, options, simple_ipc_usage, 0); + + if (cl_args.nr_threads < 1) + cl_args.nr_threads = 1; + if (cl_args.max_wait_sec < 0) + cl_args.max_wait_sec = 0; + if (cl_args.bytecount < 1) + cl_args.bytecount = 1; + if (cl_args.batchsize < 1) + cl_args.batchsize = 1; + + if (bytevalue && *bytevalue) + cl_args.bytevalue = bytevalue[0]; + + /* + * Use '!!' on all dispatch functions to map from `error()` style + * (returns -1) style to `test_must_fail` style (expects 1). This + * makes shell error messages less confusing. + */ + + if (!strcmp(cl_args.subcommand, "is-active")) + return !!client__probe_server(); + + if (!strcmp(cl_args.subcommand, "run-daemon")) + return !!daemon__run_server(); + + if (!strcmp(cl_args.subcommand, "start-daemon")) + return !!daemon__start_server(); + + /* + * Client commands follow. Ensure a server is running before + * sending any data. This might be overkill, but then again + * this is a test harness. + */ + + if (!strcmp(cl_args.subcommand, "stop-daemon")) { + if (client__probe_server()) + return 1; + return !!client__stop_server(); + } + + if (!strcmp(cl_args.subcommand, "send")) { + if (client__probe_server()) + return 1; + return !!client__send_ipc(); + } + + if (!strcmp(cl_args.subcommand, "sendbytes")) { + if (client__probe_server()) + return 1; + return !!client__sendbytes(); + } + + if (!strcmp(cl_args.subcommand, "multiple")) { + if (client__probe_server()) + return 1; + return !!client__multiple(); + } + + die("Unhandled subcommand: '%s'", cl_args.subcommand); +} +#endif diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c index bc97929..dc1c14b 100644 --- a/t/helper/test-submodule-nested-repo-config.c +++ b/t/helper/test-submodule-nested-repo-config.c @@ -1,7 +1,7 @@ #include "test-tool.h" #include "submodule-config.h" -static void die_usage(int argc, const char **argv, const char *msg) +static void die_usage(const char **argv, const char *msg) { fprintf(stderr, "%s\n", msg); fprintf(stderr, "Usage: %s <submodulepath> <config name>\n", argv[0]); @@ -11,16 +11,14 @@ static void die_usage(int argc, const char **argv, const char *msg) int cmd__submodule_nested_repo_config(int argc, const char **argv) { struct repository subrepo; - const struct submodule *sub; if (argc < 3) - die_usage(argc, argv, "Wrong number of arguments."); + die_usage(argv, "Wrong number of arguments."); setup_git_directory(); - sub = submodule_from_path(the_repository, &null_oid, argv[1]); - if (repo_submodule_init(&subrepo, the_repository, sub)) { - die_usage(argc, argv, "Submodule not found."); + if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid())) { + die_usage(argv, "Submodule not found."); } /* Read the config of _child_ submodules. */ diff --git a/t/helper/test-subprocess.c b/t/helper/test-subprocess.c index 92b69de..ff22f2f 100644 --- a/t/helper/test-subprocess.c +++ b/t/helper/test-subprocess.c @@ -15,6 +15,6 @@ int cmd__subprocess(int argc, const char **argv) argv++; } cp.git_cmd = 1; - cp.argv = (const char **)argv + 1; + strvec_pushv(&cp.args, (const char **)argv + 1); return run_command(&cp); } diff --git a/t/helper/test-svn-fe.c b/t/helper/test-svn-fe.c deleted file mode 100644 index 7667c08..0000000 --- a/t/helper/test-svn-fe.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * test-svn-fe: Code to exercise the svn import lib - */ - -#include "git-compat-util.h" -#include "vcs-svn/svndump.h" -#include "vcs-svn/svndiff.h" -#include "vcs-svn/sliding_window.h" -#include "vcs-svn/line_buffer.h" - -static const char test_svnfe_usage[] = - "test-svn-fe (<dumpfile> | [-d] <preimage> <delta> <len>)"; - -static int apply_delta(int argc, const char **argv) -{ - struct line_buffer preimage = LINE_BUFFER_INIT; - struct line_buffer delta = LINE_BUFFER_INIT; - struct sliding_view preimage_view = SLIDING_VIEW_INIT(&preimage, -1); - - if (argc != 5) - usage(test_svnfe_usage); - - if (buffer_init(&preimage, argv[2])) - die_errno("cannot open preimage"); - if (buffer_init(&delta, argv[3])) - die_errno("cannot open delta"); - if (svndiff0_apply(&delta, (off_t) strtoumax(argv[4], NULL, 0), - &preimage_view, stdout)) - return 1; - if (buffer_deinit(&preimage)) - die_errno("cannot close preimage"); - if (buffer_deinit(&delta)) - die_errno("cannot close delta"); - strbuf_release(&preimage_view.buf); - return 0; -} - -int cmd_main(int argc, const char **argv) -{ - if (argc == 2) { - if (svndump_init(argv[1])) - return 1; - svndump_read(NULL, "refs/heads/master", "refs/notes/svn/revs"); - svndump_deinit(); - svndump_reset(); - return 0; - } - - if (argc >= 2 && !strcmp(argv[1], "-d")) - return apply_delta(argc, argv); - usage(test_svnfe_usage); -} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index f20989d..318fdba 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -14,8 +14,13 @@ struct test_cmd { }; static struct test_cmd cmds[] = { + { "advise", cmd__advise_if_enabled }, + { "bitmap", cmd__bitmap }, + { "bloom", cmd__bloom }, { "chmtime", cmd__chmtime }, { "config", cmd__config }, + { "crontab", cmd__crontab }, + { "csprng", cmd__csprng }, { "ctype", cmd__ctype }, { "date", cmd__date }, { "delta", cmd__delta }, @@ -26,28 +31,41 @@ static struct test_cmd cmds[] = { { "dump-split-index", cmd__dump_split_index }, { "dump-untracked-cache", cmd__dump_untracked_cache }, { "example-decorate", cmd__example_decorate }, + { "fast-rebase", cmd__fast_rebase }, + { "fsmonitor-client", cmd__fsmonitor_client }, { "genrandom", cmd__genrandom }, { "genzeros", cmd__genzeros }, + { "getcwd", cmd__getcwd }, { "hashmap", cmd__hashmap }, { "hash-speed", cmd__hash_speed }, + { "hexdump", cmd__hexdump }, { "index-version", cmd__index_version }, { "json-writer", cmd__json_writer }, { "lazy-init-name-hash", cmd__lazy_init_name_hash }, { "match-trees", cmd__match_trees }, { "mergesort", cmd__mergesort }, { "mktemp", cmd__mktemp }, + { "oid-array", cmd__oid_array }, { "oidmap", cmd__oidmap }, + { "oidtree", cmd__oidtree }, { "online-cpus", cmd__online_cpus }, + { "pack-mtimes", cmd__pack_mtimes }, { "parse-options", cmd__parse_options }, + { "parse-pathspec-file", cmd__parse_pathspec_file }, + { "partial-clone", cmd__partial_clone }, { "path-utils", cmd__path_utils }, + { "pcre2-config", cmd__pcre2_config }, { "pkt-line", cmd__pkt_line }, { "prio-queue", cmd__prio_queue }, + { "proc-receive", cmd__proc_receive }, { "progress", cmd__progress }, { "reach", cmd__reach }, { "read-cache", cmd__read_cache }, { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, + { "dump-reftable", cmd__dump_reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking }, @@ -55,15 +73,16 @@ static struct test_cmd cmds[] = { { "scrap-cache-tree", cmd__scrap_cache_tree }, { "serve-v2", cmd__serve_v2 }, { "sha1", cmd__sha1 }, - { "sha1-array", cmd__sha1_array }, { "sha256", cmd__sha256 }, { "sigchain", cmd__sigchain }, + { "simple-ipc", cmd__simple_ipc }, { "strcmp-offset", cmd__strcmp_offset }, { "string-list", cmd__string_list }, { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, { "trace2", cmd__trace2 }, + { "userdiff", cmd__userdiff }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "xml-encode", cmd__xml_encode }, { "wildmatch", cmd__wildmatch }, @@ -110,6 +129,7 @@ int cmd_main(int argc, const char **argv) argc--; trace2_cmd_name(cmds[i].name); trace2_cmd_list_config(); + trace2_cmd_list_env_vars(); return cmds[i].fn(argc, argv); } } diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 8ed2af7..bb79927 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -4,8 +4,13 @@ #define USE_THE_INDEX_COMPATIBILITY_MACROS #include "git-compat-util.h" +int cmd__advise_if_enabled(int argc, const char **argv); +int cmd__bitmap(int argc, const char **argv); +int cmd__bloom(int argc, const char **argv); int cmd__chmtime(int argc, const char **argv); int cmd__config(int argc, const char **argv); +int cmd__crontab(int argc, const char **argv); +int cmd__csprng(int argc, const char **argv); int cmd__ctype(int argc, const char **argv); int cmd__date(int argc, const char **argv); int cmd__delta(int argc, const char **argv); @@ -15,11 +20,16 @@ int cmd__dump_cache_tree(int argc, const char **argv); int cmd__dump_fsmonitor(int argc, const char **argv); int cmd__dump_split_index(int argc, const char **argv); int cmd__dump_untracked_cache(int argc, const char **argv); +int cmd__dump_reftable(int argc, const char **argv); int cmd__example_decorate(int argc, const char **argv); +int cmd__fast_rebase(int argc, const char **argv); +int cmd__fsmonitor_client(int argc, const char **argv); int cmd__genrandom(int argc, const char **argv); int cmd__genzeros(int argc, const char **argv); +int cmd__getcwd(int argc, const char **argv); int cmd__hashmap(int argc, const char **argv); int cmd__hash_speed(int argc, const char **argv); +int cmd__hexdump(int argc, const char **argv); int cmd__index_version(int argc, const char **argv); int cmd__json_writer(int argc, const char **argv); int cmd__lazy_init_name_hash(int argc, const char **argv); @@ -27,17 +37,24 @@ int cmd__match_trees(int argc, const char **argv); int cmd__mergesort(int argc, const char **argv); int cmd__mktemp(int argc, const char **argv); int cmd__oidmap(int argc, const char **argv); +int cmd__oidtree(int argc, const char **argv); int cmd__online_cpus(int argc, const char **argv); +int cmd__pack_mtimes(int argc, const char **argv); int cmd__parse_options(int argc, const char **argv); +int cmd__parse_pathspec_file(int argc, const char** argv); +int cmd__partial_clone(int argc, const char **argv); int cmd__path_utils(int argc, const char **argv); +int cmd__pcre2_config(int argc, const char **argv); int cmd__pkt_line(int argc, const char **argv); int cmd__prio_queue(int argc, const char **argv); +int cmd__proc_receive(int argc, const char **argv); int cmd__progress(int argc, const char **argv); int cmd__reach(int argc, const char **argv); int cmd__read_cache(int argc, const char **argv); int cmd__read_graph(int argc, const char **argv); int cmd__read_midx(int argc, const char **argv); int cmd__ref_store(int argc, const char **argv); +int cmd__reftable(int argc, const char **argv); int cmd__regex(int argc, const char **argv); int cmd__repository(int argc, const char **argv); int cmd__revision_walking(int argc, const char **argv); @@ -45,15 +62,17 @@ int cmd__run_command(int argc, const char **argv); int cmd__scrap_cache_tree(int argc, const char **argv); int cmd__serve_v2(int argc, const char **argv); int cmd__sha1(int argc, const char **argv); -int cmd__sha1_array(int argc, const char **argv); +int cmd__oid_array(int argc, const char **argv); int cmd__sha256(int argc, const char **argv); int cmd__sigchain(int argc, const char **argv); +int cmd__simple_ipc(int argc, const char **argv); int cmd__strcmp_offset(int argc, const char **argv); 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__trace2(int argc, const char **argv); +int cmd__userdiff(int argc, const char **argv); int cmd__urlmatch_normalization(int argc, const char **argv); int cmd__xml_encode(int argc, const char **argv); int cmd__wildmatch(int argc, const char **argv); diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c index 197819c..a714130 100644 --- a/t/helper/test-trace2.c +++ b/t/helper/test-trace2.c @@ -1,6 +1,6 @@ #include "test-tool.h" #include "cache.h" -#include "argv-array.h" +#include "strvec.h" #include "run-command.h" #include "exec-cmd.h" #include "config.h" @@ -198,6 +198,36 @@ static int ut_006data(int argc, const char **argv) return 0; } +static int ut_007BUG(int argc, const char **argv) +{ + /* + * Exercise BUG() to ensure that the message is printed to trace2. + */ + BUG("the bug message"); +} + +static int ut_008bug(int argc, const char **argv) +{ + bug("a bug message"); + bug("another bug message"); + BUG_if_bug("an explicit BUG_if_bug() following bug() call(s) is nice, but not required"); + return 0; +} + +static int ut_009bug_BUG(int argc, const char **argv) +{ + bug("a bug message"); + bug("another bug message"); + /* The BUG_if_bug(...) isn't here, but we'll spot bug() calls on exit()! */ + return 0; +} + +static int ut_010bug_BUG(int argc, const char **argv) +{ + bug("a %s message", "bug"); + BUG("a %s message", "BUG"); +} + /* * Usage: * test-tool trace2 <ut_name_1> <ut_usage_1> @@ -214,6 +244,10 @@ static struct unit_test ut_table[] = { { ut_004child, "004child", "[<child_command_line>]" }, { ut_005exec, "005exec", "<git_command_args>" }, { ut_006data, "006data", "[<category> <key> <value>]+" }, + { ut_007BUG, "007bug", "" }, + { ut_008bug, "008bug", "" }, + { ut_009bug_BUG, "009bug_BUG","" }, + { ut_010bug_BUG, "010bug_BUG","" }, }; /* clang-format on */ @@ -253,8 +287,9 @@ static int print_usage(void) * [] the "cmd_name" event has been generated. * [] this writes various "def_param" events for interesting config values. * - * We further assume that if we return (rather than exit()), trace2_cmd_exit() - * will be called by test-tool.c:cmd_main(). + * We return from here and let test-tool.c::cmd_main() pass the exit + * code to common-main.c::main(), which will use it to call + * trace2_cmd_exit(). */ int cmd__trace2(int argc, const char **argv) { diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c new file mode 100644 index 0000000..f013f8a --- /dev/null +++ b/t/helper/test-userdiff.c @@ -0,0 +1,46 @@ +#include "test-tool.h" +#include "cache.h" +#include "userdiff.h" +#include "config.h" + +static int driver_cb(struct userdiff_driver *driver, + enum userdiff_driver_type type, void *priv) +{ + enum userdiff_driver_type *want_type = priv; + if (type & *want_type && driver->funcname.pattern) + puts(driver->name); + return 0; +} + +static int cmd__userdiff_config(const char *var, const char *value, void *cb) +{ + if (userdiff_config(var, value) < 0) + return -1; + return 0; +} + +int cmd__userdiff(int argc, const char **argv) +{ + enum userdiff_driver_type want = 0; + if (argc != 2) + return 1; + + if (!strcmp(argv[1], "list-drivers")) + want = (USERDIFF_DRIVER_TYPE_BUILTIN | + USERDIFF_DRIVER_TYPE_CUSTOM); + else if (!strcmp(argv[1], "list-builtin-drivers")) + want = USERDIFF_DRIVER_TYPE_BUILTIN; + else if (!strcmp(argv[1], "list-custom-drivers")) + want = USERDIFF_DRIVER_TYPE_CUSTOM; + else + return error("unknown argument %s", argv[1]); + + if (want & USERDIFF_DRIVER_TYPE_CUSTOM) { + setup_git_directory(); + git_config(cmd__userdiff_config, NULL); + } + + for_each_userdiff_driver(driver_cb, &want); + + return 0; +} diff --git a/t/helper/test-windows-named-pipe.c b/t/helper/test-windows-named-pipe.c index b4b752b..ae52183 100644 --- a/t/helper/test-windows-named-pipe.c +++ b/t/helper/test-windows-named-pipe.c @@ -19,7 +19,7 @@ int cmd__windows_named_pipe(int argc, const char **argv) if (argc < 2) goto print_usage; filename = argv[1]; - if (strchr(filename, '/') || strchr(filename, '\\')) + if (strpbrk(filename, "/\\")) goto print_usage; strbuf_addf(&pathname, "//./pipe/%s", filename); |