diff options
Diffstat (limited to 't')
114 files changed, 2619 insertions, 1062 deletions
diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl index b2b28c2ced..6ee7700eb4 100755 --- a/t/check-non-portable-shell.pl +++ b/t/check-non-portable-shell.pl @@ -49,8 +49,8 @@ while (<>) { /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)'; /\blocal\s+[A-Za-z0-9_]*=\$([A-Za-z0-9_{]|[(][^(])/ and err q(quote "$val" in 'local var=$val'); - /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and - err '"FOO=bar shell_func" assignment extends beyond "shell_func"'; + /\b([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and !/test_env.+=/ and exists($func{$4}) and + err '"FOO=bar shell_func" is not portable (use test_env FOO=bar shell_func)'; $line = ''; # this resets our $. for each file close ARGV if eof; diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c index 2912899558..195e6278be 100644 --- a/t/helper/test-hashmap.c +++ b/t/helper/test-hashmap.c @@ -12,11 +12,6 @@ struct test_entry char key[FLEX_ARRAY]; }; -static const char *get_value(const struct test_entry *e) -{ - return e->key + strlen(e->key) + 1; -} - static int test_entry_cmp(const void *cmp_data, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, @@ -141,30 +136,16 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) /* * Read stdin line by line and print result of commands to stdout: * - * hash key -> strhash(key) memhash(key) strihash(key) memihash(key) - * put key value -> NULL / old value - * get key -> NULL / value - * remove key -> NULL / old value - * iterate -> key1 value1\nkey2 value2\n... - * size -> tablesize numentries - * * perfhashmap method rounds -> test hashmap.[ch] performance */ int cmd__hashmap(int argc, const char **argv) { struct string_list parts = STRING_LIST_INIT_NODUP; struct strbuf line = STRBUF_INIT; - int icase; - struct hashmap map = HASHMAP_INIT(test_entry_cmp, &icase); - - /* init hash map */ - icase = argc > 1 && !strcmp("ignorecase", argv[1]); /* process commands from stdin */ while (strbuf_getline(&line, stdin) != EOF) { char *cmd, *p1, *p2; - unsigned int hash = 0; - struct test_entry *entry; /* break line into command and up to two parameters */ string_list_setlen(&parts, 0); @@ -180,84 +161,8 @@ int cmd__hashmap(int argc, const char **argv) cmd = parts.items[0].string; p1 = parts.nr >= 1 ? parts.items[1].string : NULL; p2 = parts.nr >= 2 ? parts.items[2].string : NULL; - if (p1) - hash = icase ? strihash(p1) : strhash(p1); - - if (!strcmp("add", cmd) && p1 && p2) { - - /* create entry with key = p1, value = p2 */ - entry = alloc_test_entry(hash, p1, p2); - - /* add to hashmap */ - hashmap_add(&map, &entry->ent); - - } else if (!strcmp("put", cmd) && p1 && p2) { - - /* create entry with key = p1, value = p2 */ - entry = alloc_test_entry(hash, p1, p2); - - /* add / replace entry */ - entry = hashmap_put_entry(&map, entry, ent); - - /* print and free replaced entry, if any */ - puts(entry ? get_value(entry) : "NULL"); - free(entry); - - } else if (!strcmp("get", cmd) && p1) { - /* lookup entry in hashmap */ - entry = hashmap_get_entry_from_hash(&map, hash, p1, - struct test_entry, ent); - - /* print result */ - if (!entry) - puts("NULL"); - hashmap_for_each_entry_from(&map, entry, ent) - puts(get_value(entry)); - - } else if (!strcmp("remove", cmd) && p1) { - - /* setup static key */ - struct hashmap_entry key; - struct hashmap_entry *rm; - hashmap_entry_init(&key, hash); - - /* remove entry from hashmap */ - rm = hashmap_remove(&map, &key, p1); - entry = rm ? container_of(rm, struct test_entry, ent) - : NULL; - - /* print result and free entry*/ - puts(entry ? get_value(entry) : "NULL"); - free(entry); - - } else if (!strcmp("iterate", cmd)) { - struct hashmap_iter iter; - - hashmap_for_each_entry(&map, &iter, entry, - ent /* member name */) - printf("%s %s\n", entry->key, get_value(entry)); - - } else if (!strcmp("size", cmd)) { - - /* print table sizes */ - printf("%u %u\n", map.tablesize, - hashmap_get_size(&map)); - - } else if (!strcmp("intern", cmd) && p1) { - - /* test that strintern works */ - const char *i1 = strintern(p1); - const char *i2 = strintern(p1); - if (strcmp(i1, p1)) - printf("strintern(%s) returns %s\n", p1, i1); - else if (i1 == p1) - printf("strintern(%s) returns input pointer\n", p1); - else if (i1 != i2) - printf("strintern(%s) != strintern(%s)", i1, i2); - else - printf("%s\n", i1); - } else if (!strcmp("perfhashmap", cmd) && p1 && p2) { + if (!strcmp("perfhashmap", cmd) && p1 && p2) { perf_hashmap(atoi(p1), atoi(p2)); @@ -270,6 +175,5 @@ int cmd__hashmap(int argc, const char **argv) string_list_clear(&parts, 0); strbuf_release(&line); - hashmap_clear_and_free(&map, struct test_entry, ent); return 0; } diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c index ed52eb76bf..a288069b04 100644 --- a/t/helper/test-json-writer.c +++ b/t/helper/test-json-writer.c @@ -415,6 +415,7 @@ static void get_i(struct line *line, intmax_t *s_in) get_s(line, &s); + errno = 0; *s_in = strtol(s, &endptr, 10); if (*endptr || errno == ERANGE) die("line[%d]: invalid integer value", line->nr); @@ -427,6 +428,7 @@ static void get_d(struct line *line, double *s_in) get_s(line, &s); + errno = 0; *s_in = strtod(s, &endptr); if (*endptr || errno == ERANGE) die("line[%d]: invalid float value", line->nr); diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index 637b8b294e..65346dee55 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -156,7 +156,7 @@ static int cmd_rename_ref(struct ref_store *refs, const char **argv) return refs_rename_ref(refs, oldref, newref, logmsg); } -static int each_ref(const char *refname, const struct object_id *oid, +static int each_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, int flags, void *cb_data UNUSED) { printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags); diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c index aa6538a8da..623cf3f0f5 100644 --- a/t/helper/test-reftable.c +++ b/t/helper/test-reftable.c @@ -6,10 +6,7 @@ int cmd__reftable(int argc, const char **argv) { /* test from simple to complex. */ 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); return 0; } diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c index c6a074df3d..63c37de33d 100644 --- a/t/helper/test-repository.c +++ b/t/helper/test-repository.c @@ -19,7 +19,7 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree, setup_git_env(gitdir); - memset(the_repository, 0, sizeof(*the_repository)); + repo_clear(the_repository); if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); @@ -49,7 +49,7 @@ static void test_get_commit_tree_in_graph(const char *gitdir, setup_git_env(gitdir); - memset(the_repository, 0, sizeof(*the_repository)); + repo_clear(the_repository); if (repo_init(&r, gitdir, worktree)) die("Couldn't init repo"); diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c index cd955ec63e..c588c273ce 100644 --- a/t/helper/test-trace2.c +++ b/t/helper/test-trace2.c @@ -26,6 +26,7 @@ static int get_i(int *p_value, const char *data) if (!data || !*data) return MyError; + errno = 0; *p_value = strtol(data, &endptr, 10); if (*endptr || errno == ERANGE) return MyError; diff --git a/t/socks4-proxy.pl b/t/socks4-proxy.pl new file mode 100644 index 0000000000..4c3a35c008 --- /dev/null +++ b/t/socks4-proxy.pl @@ -0,0 +1,48 @@ +use strict; +use IO::Select; +use IO::Socket::UNIX; +use IO::Socket::INET; + +my $path = shift; + +unlink($path); +my $server = IO::Socket::UNIX->new(Listen => 1, Local => $path) + or die "unable to listen on $path: $!"; + +$| = 1; +print "ready\n"; + +while (my $client = $server->accept()) { + sysread $client, my $buf, 8; + my ($version, $cmd, $port, $ip) = unpack 'CCnN', $buf; + next unless $version == 4; # socks4 + next unless $cmd == 1; # TCP stream connection + + # skip NUL-terminated id + while (sysread $client, my $char, 1) { + last unless ord($char); + } + + # version(0), reply(5a == granted), port (ignored), ip (ignored) + syswrite $client, "\x00\x5a\x00\x00\x00\x00\x00\x00"; + + my $remote = IO::Socket::INET->new(PeerHost => $ip, PeerPort => $port) + or die "unable to connect to $ip/$port: $!"; + + my $io = IO::Select->new($client, $remote); + while ($io->count) { + for my $fh ($io->can_read(0)) { + for my $pair ([$client, $remote], [$remote, $client]) { + my ($from, $to) = @$pair; + next unless $fh == $from; + + my $r = sysread $from, my $buf, 1024; + if (!defined $r || $r <= 0) { + $io->remove($from); + next; + } + syswrite $to, $buf; + } + } + } +} diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh deleted file mode 100755 index 46e74ad107..0000000000 --- a/t/t0011-hashmap.sh +++ /dev/null @@ -1,260 +0,0 @@ -#!/bin/sh - -test_description='test hashmap and string hash functions' - -TEST_PASSES_SANITIZE_LEAK=true -. ./test-lib.sh - -test_hashmap() { - echo "$1" | test-tool hashmap $3 > actual && - echo "$2" > expect && - test_cmp expect actual -} - -test_expect_success 'put' ' - -test_hashmap "put key1 value1 -put key2 value2 -put fooBarFrotz value3 -put foobarfrotz value4 -size" "NULL -NULL -NULL -NULL -64 4" - -' - -test_expect_success 'put (case insensitive)' ' - -test_hashmap "put key1 value1 -put key2 value2 -put fooBarFrotz value3 -size" "NULL -NULL -NULL -64 3" ignorecase - -' - -test_expect_success 'replace' ' - -test_hashmap "put key1 value1 -put key1 value2 -put fooBarFrotz value3 -put fooBarFrotz value4 -size" "NULL -value1 -NULL -value3 -64 2" - -' - -test_expect_success 'replace (case insensitive)' ' - -test_hashmap "put key1 value1 -put Key1 value2 -put fooBarFrotz value3 -put foobarfrotz value4 -size" "NULL -value1 -NULL -value3 -64 2" ignorecase - -' - -test_expect_success 'get' ' - -test_hashmap "put key1 value1 -put key2 value2 -put fooBarFrotz value3 -put foobarfrotz value4 -get key1 -get key2 -get fooBarFrotz -get notInMap" "NULL -NULL -NULL -NULL -value1 -value2 -value3 -NULL" - -' - -test_expect_success 'get (case insensitive)' ' - -test_hashmap "put key1 value1 -put key2 value2 -put fooBarFrotz value3 -get Key1 -get keY2 -get foobarfrotz -get notInMap" "NULL -NULL -NULL -value1 -value2 -value3 -NULL" ignorecase - -' - -test_expect_success 'add' ' - -test_hashmap "add key1 value1 -add key1 value2 -add fooBarFrotz value3 -add fooBarFrotz value4 -get key1 -get fooBarFrotz -get notInMap" "value2 -value1 -value4 -value3 -NULL" - -' - -test_expect_success 'add (case insensitive)' ' - -test_hashmap "add key1 value1 -add Key1 value2 -add fooBarFrotz value3 -add foobarfrotz value4 -get key1 -get Foobarfrotz -get notInMap" "value2 -value1 -value4 -value3 -NULL" ignorecase - -' - -test_expect_success 'remove' ' - -test_hashmap "put key1 value1 -put key2 value2 -put fooBarFrotz value3 -remove key1 -remove key2 -remove notInMap -size" "NULL -NULL -NULL -value1 -value2 -NULL -64 1" - -' - -test_expect_success 'remove (case insensitive)' ' - -test_hashmap "put key1 value1 -put key2 value2 -put fooBarFrotz value3 -remove Key1 -remove keY2 -remove notInMap -size" "NULL -NULL -NULL -value1 -value2 -NULL -64 1" ignorecase - -' - -test_expect_success 'iterate' ' - test-tool hashmap >actual.raw <<-\EOF && - put key1 value1 - put key2 value2 - put fooBarFrotz value3 - iterate - EOF - - cat >expect <<-\EOF && - NULL - NULL - NULL - fooBarFrotz value3 - key1 value1 - key2 value2 - EOF - - sort <actual.raw >actual && - test_cmp expect actual -' - -test_expect_success 'iterate (case insensitive)' ' - test-tool hashmap ignorecase >actual.raw <<-\EOF && - put key1 value1 - put key2 value2 - put fooBarFrotz value3 - iterate - EOF - - cat >expect <<-\EOF && - NULL - NULL - NULL - fooBarFrotz value3 - key1 value1 - key2 value2 - EOF - - sort <actual.raw >actual && - test_cmp expect actual -' - -test_expect_success 'grow / shrink' ' - - rm -f in && - rm -f expect && - for n in $(test_seq 51) - do - echo put key$n value$n >> in && - echo NULL >> expect || return 1 - done && - echo size >> in && - echo 64 51 >> expect && - echo put key52 value52 >> in && - echo NULL >> expect && - echo size >> in && - echo 256 52 >> expect && - for n in $(test_seq 12) - do - echo remove key$n >> in && - echo value$n >> expect || return 1 - done && - echo size >> in && - echo 256 40 >> expect && - echo remove key40 >> in && - echo value40 >> expect && - echo size >> in && - echo 64 39 >> expect && - test-tool hashmap <in >out && - test_cmp expect out - -' - -test_expect_success 'string interning' ' - -test_hashmap "intern value1 -intern Value1 -intern value2 -intern value2 -" "value1 -Value1 -value2 -value2" - -' - -test_done diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh index 29306b367c..fac52322a7 100755 --- a/t/t0018-advice.sh +++ b/t/t0018-advice.sh @@ -96,7 +96,6 @@ test_expect_success 'advice should be printed when GIT_ADVICE is set to true' ' >README && GIT_ADVICE=true git status ) >actual && - cat actual > /tmp/actual && test_cmp expect actual ' diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 0b4997022b..eeb2714d9d 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -5,6 +5,7 @@ test_description='blob conversion via gitattributes' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-terminal.sh diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh index 5fe61f1291..e97a84764f 100755 --- a/t/t0033-safe-directory.sh +++ b/t/t0033-safe-directory.sh @@ -119,4 +119,182 @@ test_expect_success 'local clone of unowned repo accepted in safe directory' ' test_path_is_dir target ' +test_expect_success SYMLINKS 'checked paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + git init repository && + ln -s repository repo && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repository" + ) && + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repo for-each-ref && + git -C repo/ for-each-ref && + test_must_fail git -C repository/.git for-each-ref && + test_must_fail git -C repository/.git/ for-each-ref && + test_must_fail git -C repo/.git for-each-ref && + test_must_fail git -C repo/.git/ for-each-ref +' + +test_expect_success SYMLINKS 'checked leading paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository && + git init repository/s && + ln -s repository repo && + ( + cd repository/s && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repository/*" + ) && + git -C repository/s for-each-ref && + git -C repository/s/ for-each-ref && + git -C repo/s for-each-ref && + git -C repo/s/ for-each-ref && + git -C repository/s/.git for-each-ref && + git -C repository/s/.git/ for-each-ref && + git -C repo/s/.git for-each-ref && + git -C repo/s/.git/ for-each-ref +' + +test_expect_success SYMLINKS 'configured paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + git init repository && + ln -s repository repo && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repo" + ) && + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repo for-each-ref && + git -C repo/ for-each-ref && + test_must_fail git -C repository/.git for-each-ref && + test_must_fail git -C repository/.git/ for-each-ref && + test_must_fail git -C repo/.git for-each-ref && + test_must_fail git -C repo/.git/ for-each-ref +' + +test_expect_success SYMLINKS 'configured leading paths are normalized' ' + test_when_finished "rm -rf repository; rm -f repo" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository && + git init repository/s && + ln -s repository repo && + ( + cd repository/s && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "$(pwd)/repo/*" + ) && + git -C repository/s for-each-ref && + git -C repository/s/ for-each-ref && + git -C repository/s/.git for-each-ref && + git -C repository/s/.git/ for-each-ref && + git -C repo/s for-each-ref && + git -C repo/s/ for-each-ref && + git -C repo/s/.git for-each-ref && + git -C repo/s/.git/ for-each-ref +' + +test_expect_success 'safe.directory set to a dot' ' + test_when_finished "rm -rf repository" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository/subdir && + git init repository && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "." + ) && + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repository/.git for-each-ref && + git -C repository/.git/ for-each-ref && + + # What is allowed is repository/subdir but the repository + # path is repository. + test_must_fail git -C repository/subdir for-each-ref && + + # Likewise, repository .git/refs is allowed with "." but + # repository/.git that is accessed is not allowed. + test_must_fail git -C repository/.git/refs for-each-ref +' + +test_expect_success 'safe.directory set to asterisk' ' + test_when_finished "rm -rf repository" && + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global --unset-all safe.directory + ) && + mkdir -p repository/subdir && + git init repository && + ( + cd repository && + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + test_commit sample + ) && + + ( + sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER && + git config --global safe.directory "*" + ) && + # these are trivial + git -C repository for-each-ref && + git -C repository/ for-each-ref && + git -C repository/.git for-each-ref && + git -C repository/.git/ for-each-ref && + + # With "*", everything is allowed, and the repository is + # discovered, which is different behaviour from "." above. + git -C repository/subdir for-each-ref && + + # Likewise. + git -C repository/.git/refs for-each-ref +' + test_done diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index c10e35905e..5d5b64205f 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='credential-cache tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh index 716bf1af9f..f83db659e2 100755 --- a/t/t0302-credential-store.sh +++ b/t/t0302-credential-store.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='credential-store tests' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh index 72ae405c3e..8aadbe86c4 100755 --- a/t/t0303-credential-external.sh +++ b/t/t0303-credential-external.sh @@ -29,6 +29,7 @@ you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell commands. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-credential.sh diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh index b06c46999d..37510c2b2a 100755 --- a/t/t0610-reftable-basics.sh +++ b/t/t0610-reftable-basics.sh @@ -478,19 +478,26 @@ test_expect_success "$command: auto compaction" ' test_oid blob17_2 | git hash-object -w --stdin && - # Lock all tables write some refs. Auto-compaction will be - # unable to compact tables and thus fails gracefully, leaving - # the stack in a sub-optimal state. - ls .git/reftable/*.ref | + # Lock all tables, write some refs. Auto-compaction will be + # unable to compact tables and thus fails gracefully, + # compacting only those tables which are not locked. + ls .git/reftable/*.ref | sort | while read table do - touch "$table.lock" || exit 1 + touch "$table.lock" && + basename "$table" >>tables.expect || exit 1 done && + test_line_count = 2 .git/reftable/tables.list && git branch B && git branch C && - rm .git/reftable/*.lock && - test_line_count = 4 .git/reftable/tables.list && + # The new tables are auto-compacted, but the locked tables are + # left intact. + test_line_count = 3 .git/reftable/tables.list && + head -n 2 .git/reftable/tables.list >tables.head && + test_cmp tables.expect tables.head && + + rm .git/reftable/*.lock && git $command --auto && test_line_count = 1 .git/reftable/tables.list ) diff --git a/t/t1300-config.sh b/t/t1300-config.sh index 9de2d95f06..f13277c8f3 100755 --- a/t/t1300-config.sh +++ b/t/t1300-config.sh @@ -2704,6 +2704,15 @@ test_expect_success '--get and --get-all with --fixed-value' ' test_must_fail git config --file=config --get-regexp --fixed-value fixed+ non-existent ' +test_expect_success '--fixed-value with value-less configuration' ' + test_when_finished rm -f config && + cat >config <<-\EOF && + [section] + key + EOF + git config --file=config --fixed-value section.key value pattern +' + test_expect_success 'includeIf.hasconfig:remote.*.url' ' git init hasremoteurlTest && test_when_finished "rm -rf hasremoteurlTest" && diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 5bf883f1e3..246a3f46ab 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -146,6 +146,14 @@ test_expect_success rewind ' test_line_count = 5 output ' +test_expect_success 'reflog expire should not barf on an annotated tag' ' + test_when_finished "git tag -d v0.tag || :" && + git -c core.logAllRefUpdates=always \ + tag -a -m "tag name" v0.tag main && + git reflog expire --dry-run refs/tags/v0.tag 2>err && + test_grep ! "error: [Oo]bject .* not a commit" err +' + test_expect_success 'corrupt and check' ' corrupt $F && diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index b754b9fd74..5eaa6428c4 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test git rev-parse --parseopt' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_invalid_long_option () { diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh index 6ecfed86bc..e7e78a4028 100755 --- a/t/t1511-rev-parse-caret.sh +++ b/t/t1511-rev-parse-caret.sh @@ -5,6 +5,7 @@ test_description='tests for ref^{stuff}' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index be3fcdde07..b3f6bc97b5 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh @@ -5,6 +5,7 @@ test_description='undoing resolution' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_resolve_undo () { diff --git a/t/t2080-parallel-checkout-basics.sh b/t/t2080-parallel-checkout-basics.sh index 5ffe1a41e2..59e5570cb2 100755 --- a/t/t2080-parallel-checkout-basics.sh +++ b/t/t2080-parallel-checkout-basics.sh @@ -8,6 +8,7 @@ working tree. ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" diff --git a/t/t2082-parallel-checkout-attributes.sh b/t/t2082-parallel-checkout-attributes.sh index f3511cd43a..aec55496eb 100755 --- a/t/t2082-parallel-checkout-attributes.sh +++ b/t/t2082-parallel-checkout-attributes.sh @@ -10,6 +10,7 @@ properly (without access to the index or attribute stack). ' TEST_NO_CREATE_REPO=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY/lib-parallel-checkout.sh" . "$TEST_DIRECTORY/lib-encoding.sh" diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index cc72ead79f..f0eab13f96 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -5,6 +5,7 @@ test_description='basic update-index tests Tests for command-line parsing and basic operation. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'update-index --nonsense fails' ' diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index ba320dc417..cfc4aeb179 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -6,6 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME TEST_CREATE_REPO_NO_TEMPLATE=1 +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-rebase.sh diff --git a/t/t2501-cwd-empty.sh b/t/t2501-cwd-empty.sh index f6d8d7d03d..8af4e8cfe3 100755 --- a/t/t2501-cwd-empty.sh +++ b/t/t2501-cwd-empty.sh @@ -2,6 +2,7 @@ test_description='Test handling of the current working directory becoming empty' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 800fc33165..6e587d27d7 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -2,6 +2,7 @@ test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh index a1139f79e2..3b6dad0c46 100755 --- a/t/t3202-show-branch.sh +++ b/t/t3202-show-branch.sh @@ -2,6 +2,7 @@ test_description='test show-branch' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'error descriptions on empty repository' ' diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index a767c3520e..86010931ab 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -5,6 +5,7 @@ test_description='range-diff tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Note that because of the range-diff's heuristics, test_commit does more @@ -533,9 +534,9 @@ test_expect_success 'dual-coloring' ' for prev in topic main..topic do test_expect_success "format-patch --range-diff=$prev" ' + test_when_finished "rm -f 000?-*" && git format-patch --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && grep "= 1: .* s/5/A" 0000-* && @@ -560,32 +561,32 @@ test_expect_success "explicit --no-cover-letter defeats implied --cover-letter" ' test_expect_success 'format-patch --range-diff as commentary' ' + test_when_finished "rm -f 0001-*" && git format-patch --range-diff=HEAD~1 HEAD~1 >actual && - test_when_finished "rm 0001-*" && test_line_count = 1 actual && test_grep "^Range-diff:$" 0001-* && grep "> 1: .* new message" 0001-* ' test_expect_success 'format-patch --range-diff reroll-count with a non-integer' ' + test_when_finished "rm -f v2.9-0001-*" && git format-patch --range-diff=HEAD~1 -v2.9 HEAD~1 >actual && - test_when_finished "rm v2.9-0001-*" && test_line_count = 1 actual && test_grep "^Range-diff:$" v2.9-0001-* && grep "> 1: .* new message" v2.9-0001-* ' test_expect_success 'format-patch --range-diff reroll-count with a integer' ' + test_when_finished "rm -f v2-0001-*" && git format-patch --range-diff=HEAD~1 -v2 HEAD~1 >actual && - test_when_finished "rm v2-0001-*" && test_line_count = 1 actual && test_grep "^Range-diff ..* v1:$" v2-0001-* && grep "> 1: .* new message" v2-0001-* ' test_expect_success 'format-patch --range-diff with v0' ' + test_when_finished "rm -f v0-0001-*" && git format-patch --range-diff=HEAD~1 -v0 HEAD~1 >actual && - test_when_finished "rm v0-0001-*" && test_line_count = 1 actual && test_grep "^Range-diff:$" v0-0001-* && grep "> 1: .* new message" v0-0001-* @@ -606,9 +607,9 @@ test_expect_success 'basic with modified format.pretty without "commit "' ' ' test_expect_success 'range-diff compares notes by default' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && git range-diff --no-color main..topic main..unmodified \ >actual && sed s/Z/\ /g >expect <<-EOF && @@ -630,9 +631,9 @@ test_expect_success 'range-diff compares notes by default' ' ' test_expect_success 'range-diff with --no-notes' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && git range-diff --no-color --no-notes main..topic main..unmodified \ >actual && cat >expect <<-EOF && @@ -645,12 +646,12 @@ test_expect_success 'range-diff with --no-notes' ' ' test_expect_success 'range-diff with multiple --notes' ' + test_when_finished "git notes --ref=note1 remove topic unmodified || :" && git notes --ref=note1 add -m "topic note1" topic && git notes --ref=note1 add -m "unmodified note1" unmodified && - test_when_finished git notes --ref=note1 remove topic unmodified && + test_when_finished "git notes --ref=note2 remove topic unmodified || :" && git notes --ref=note2 add -m "topic note2" topic && git notes --ref=note2 add -m "unmodified note2" unmodified && - test_when_finished git notes --ref=note2 remove topic unmodified && git range-diff --no-color --notes=note1 --notes=note2 main..topic main..unmodified \ >actual && sed s/Z/\ /g >expect <<-EOF && @@ -678,12 +679,12 @@ test_expect_success 'range-diff with multiple --notes' ' # `range-diff` should act like `log` with regards to notes test_expect_success 'range-diff with --notes=custom does not show default notes' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && + test_when_finished "git notes --ref=custom remove topic unmodified || :" && git notes --ref=custom add -m "topic note" topic && git notes --ref=custom add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && - test_when_finished git notes --ref=custom remove topic unmodified && git range-diff --notes=custom main..topic main..unmodified \ >actual && ! grep "## Notes ##" actual && @@ -691,12 +692,12 @@ test_expect_success 'range-diff with --notes=custom does not show default notes' ' test_expect_success 'format-patch --range-diff does not compare notes by default' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && + test_when_finished "rm -f 000?-*" && git format-patch --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && grep "= 1: .* s/5/A" 0000-* && @@ -708,26 +709,26 @@ test_expect_success 'format-patch --range-diff does not compare notes by default ' test_expect_success 'format-patch --notes=custom --range-diff only compares custom notes' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && - git notes --ref=custom add -m "topic note (custom)" topic && git notes add -m "unmodified note" unmodified && + test_when_finished "git notes --ref=custom remove topic unmodified || :" && + git notes --ref=custom add -m "topic note (custom)" topic && git notes --ref=custom add -m "unmodified note (custom)" unmodified && - test_when_finished git notes remove topic unmodified && - test_when_finished git notes --ref=custom remove topic unmodified && + test_when_finished "rm -f 000?-*" && git format-patch --notes=custom --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && grep "## Notes (custom) ##" 0000-* && ! grep "## Notes ##" 0000-* ' test_expect_success 'format-patch --range-diff with --no-notes' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && + test_when_finished "rm -f 000?-*" && git format-patch --no-notes --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && grep "= 1: .* s/5/A" 0000-* && @@ -739,12 +740,12 @@ test_expect_success 'format-patch --range-diff with --no-notes' ' ' test_expect_success 'format-patch --range-diff with --notes' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && + test_when_finished "rm -f 000?-*" && git format-patch --notes --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && grep "= 1: .* s/5/A" 0000-* && @@ -767,13 +768,13 @@ test_expect_success 'format-patch --range-diff with --notes' ' ' test_expect_success 'format-patch --range-diff with format.notes config' ' + test_when_finished "git notes remove topic unmodified || :" && git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && - test_when_finished git notes remove topic unmodified && test_config format.notes true && + test_when_finished "rm -f 000?-*" && git format-patch --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && grep "= 1: .* s/5/A" 0000-* && @@ -796,15 +797,15 @@ test_expect_success 'format-patch --range-diff with format.notes config' ' ' test_expect_success 'format-patch --range-diff with multiple notes' ' + test_when_finished "git notes --ref=note1 remove topic unmodified || :" && git notes --ref=note1 add -m "topic note1" topic && git notes --ref=note1 add -m "unmodified note1" unmodified && - test_when_finished git notes --ref=note1 remove topic unmodified && + test_when_finished "git notes --ref=note2 remove topic unmodified || :" && git notes --ref=note2 add -m "topic note2" topic && git notes --ref=note2 add -m "unmodified note2" unmodified && - test_when_finished git notes --ref=note2 remove topic unmodified && + test_when_finished "rm -f 000?-*" && git format-patch --notes=note1 --notes=note2 --cover-letter --range-diff=$prev \ main..unmodified >actual && - test_when_finished "rm 000?-*" && test_line_count = 5 actual && test_grep "^Range-diff:$" 0000-* && grep "= 1: .* s/5/A" 0000-* && diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index 536bd11ff4..99137fb235 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh @@ -1557,4 +1557,14 @@ test_expect_success 'empty notes are displayed by git log' ' test_cmp expect actual ' +test_expect_success 'empty notes do not invoke the editor' ' + test_commit 18th && + GIT_EDITOR="false" git notes add -C "$empty_blob" --allow-empty && + git notes remove HEAD && + GIT_EDITOR="false" git notes add -m "" --allow-empty && + git notes remove HEAD && + GIT_EDITOR="false" git notes add -F /dev/null --allow-empty && + git notes remove HEAD +' + test_done diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 36ca126bcd..2aa8593f77 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -392,8 +392,7 @@ test_expect_success 'refuse to merge ancestors of HEAD' ' test_expect_success 'root commits' ' git checkout --orphan unrelated && - (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \ - test_commit second-root) && + test_commit --author "Parsnip <root@example.com>" second-root && test_commit third-root && cat >script-from-scratch <<-\EOF && pick third-root diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index 389670262e..12bd3db4cb 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -5,6 +5,7 @@ test_description='basic git replay tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh GIT_AUTHOR_NAME=author@name diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 5d78868ac1..718438ffc7 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -575,6 +575,54 @@ test_expect_success 'navigate to hunk via regex / pattern' ' test_cmp expect actual.trimmed ' +test_expect_success 'print again the hunk' ' + test_when_finished "git reset" && + tr _ " " >expect <<-EOF && + +15 + 20 + (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? @@ -1,2 +1,3 @@ + 10 + +15 + 20 + (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]?_ + EOF + test_write_lines s y g 1 p | git add -p >actual && + tail -n 7 <actual >actual.trimmed && + test_cmp expect actual.trimmed +' + +test_expect_success TTY 'print again the hunk (PAGER)' ' + test_when_finished "git reset" && + cat >expect <<-EOF && + <GREEN>+<RESET><GREEN>15<RESET> + 20<RESET> + <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET>PAGER <CYAN>@@ -1,2 +1,3 @@<RESET> + PAGER 10<RESET> + PAGER <GREEN>+<RESET><GREEN>15<RESET> + PAGER 20<RESET> + <BOLD;BLUE>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,p,?]? <RESET> + EOF + test_write_lines s y g 1 P | + ( + GIT_PAGER="sed s/^/PAGER\ /" && + export GIT_PAGER && + test_terminal git add -p >actual + ) && + tail -n 7 <actual | test_decode_color >actual.trimmed && + test_cmp expect actual.trimmed +' + +test_expect_success TTY 'P handles SIGPIPE when writing to pager' ' + test_when_finished "rm -f huge_file; git reset" && + printf "\n%2500000s" Y >huge_file && + git add -N huge_file && + test_write_lines P q | ( + GIT_PAGER="head -n 1" && + export GIT_PAGER && + test_terminal git add -p + ) +' + test_expect_success 'split hunk "add -p (edit)"' ' # Split, say Edit and do nothing. Then: # @@ -1164,4 +1212,23 @@ test_expect_success 'reset -p with unmerged files' ' test_must_be_empty staged ' +test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' + test_config diff.suppressBlankEmpty true && + write_script fake-editor.sh <<-\EOF && + tr F G <"$1" >"$1.tmp" && + mv "$1.tmp" "$1" + EOF + + test_write_lines a b "" c d "" e f "" >file && + git add file && + test_write_lines A b "" c D "" e F "" >file && + ( + test_set_editor "$(pwd)/fake-editor.sh" && + test_write_lines s n y e q | git add -p file + ) && + git cat-file blob :file >actual && + test_write_lines a b "" c D "" e G "" >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index a7f71f8126..e4c0937f61 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -8,6 +8,7 @@ test_description='Test git stash' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-unique-files.sh diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh index 368fc2a6cc..aa5019fd6c 100755 --- a/t/t3904-stash-patch.sh +++ b/t/t3904-stash-patch.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='stash -p' + +TEST_PASSES_SANITIZE_LEAK=true . ./lib-patch-mode.sh test_expect_success 'setup' ' diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh index 1289ae3e07..a1733f45c3 100755 --- a/t/t3905-stash-include-untracked.sh +++ b/t/t3905-stash-include-untracked.sh @@ -5,6 +5,7 @@ test_description='Test git stash --include-untracked' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'stash save --include-untracked some dirty working directory' ' diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 74586f3813..4dcd7e9925 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -70,7 +70,7 @@ test_language_driver () { word_diff --color-words ' test_expect_success "diff driver '$lang' in Islandic" ' - LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \ + test_env LANG=is_IS.UTF-8 LANGUAGE=is LC_ALL="$is_IS_locale" \ word_diff --color-words ' } diff --git a/t/t4069-remerge-diff.sh b/t/t4069-remerge-diff.sh index 07323ebafe..ca8f999cab 100755 --- a/t/t4069-remerge-diff.sh +++ b/t/t4069-remerge-diff.sh @@ -110,6 +110,41 @@ test_expect_success 'can filter out additional headers with pickaxe' ' test_must_be_empty actual ' +test_expect_success 'remerge-diff also works for git-diff-tree' ' + # With a clean merge + git diff-tree -r -p --remerge-diff --no-commit-id bc_resolution >actual && + test_must_be_empty actual && + + # With both a resolved conflict and an unrelated change + cat <<-EOF >tmp && + diff --git a/numbers b/numbers + remerge CONFLICT (content): Merge conflict in numbers + index a1fb731..6875544 100644 + --- a/numbers + +++ b/numbers + @@ -1,13 +1,9 @@ + 1 + 2 + -<<<<<<< b0ed5cb (change_a) + -three + -======= + -tres + ->>>>>>> 6cd3f82 (change_b) + +drei + 4 + 5 + 6 + 7 + -eight + +acht + 9 + EOF + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >expect && + git diff-tree -r -p --remerge-diff --no-commit-id ab_resolution >tmp && + sed -e "s/[0-9a-f]\{7,\}/HASH/g" tmp >actual && + test_cmp expect actual +' + test_expect_success 'setup non-content conflicts' ' git switch --orphan base && diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh index 4eb8444029..d9a1084b5e 100755 --- a/t/t4129-apply-samemode.sh +++ b/t/t4129-apply-samemode.sh @@ -130,4 +130,66 @@ test_expect_success 'git apply respects core.fileMode' ' test_grep ! "has type 100644, expected 100755" err ' +test_expect_success POSIXPERM 'patch mode for new file is canonicalized' ' + cat >patch <<-\EOF && + diff --git a/non-canon b/non-canon + new file mode 100660 + --- /dev/null + +++ b/non-canon + +content + EOF + test_when_finished "git reset --hard" && + ( + umask 0 && + git apply --index patch 2>err + ) && + test_must_be_empty err && + git ls-files -s -- non-canon >staged && + test_grep "^100644" staged && + ls -l non-canon >worktree && + test_grep "^-rw-rw-rw" worktree +' + +test_expect_success POSIXPERM 'patch mode for deleted file is canonicalized' ' + test_when_finished "git reset --hard" && + echo content >non-canon && + git add non-canon && + chmod 666 non-canon && + + cat >patch <<-\EOF && + diff --git a/non-canon b/non-canon + deleted file mode 100660 + --- a/non-canon + +++ /dev/null + @@ -1 +0,0 @@ + -content + EOF + git apply --index patch 2>err && + test_must_be_empty err && + git ls-files -- non-canon >staged && + test_must_be_empty staged && + test_path_is_missing non-canon +' + +test_expect_success POSIXPERM 'patch mode for mode change is canonicalized' ' + test_when_finished "git reset --hard" && + echo content >non-canon && + git add non-canon && + + cat >patch <<-\EOF && + diff --git a/non-canon b/non-canon + old mode 100660 + new mode 100770 + EOF + ( + umask 0 && + git apply --index patch 2>err + ) && + test_must_be_empty err && + git ls-files -s -- non-canon >staged && + test_grep "^100755" staged && + ls -l non-canon >worktree && + test_grep "^-rwxrwxrwx" worktree +' + test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index b0a3e84984..213b36fb96 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -25,6 +25,7 @@ test_description='git rerere GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index f698d0c9ad..c20c885724 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -9,6 +9,7 @@ test_description='git shortlog GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 8a88dd7900..79e5f42760 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -5,6 +5,7 @@ test_description='.mailmap configurations' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup commits and contacts file' ' diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 605faea0c7..dc8ddb10af 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -114,6 +114,46 @@ test_expect_success 'patch-id supports git-format-patch output' ' test "$2" = $(git rev-parse HEAD) ' +test_expect_success 'patch-id computes the same for various formats' ' + # This test happens to consider "git log -p -1" output + # the canonical input format, so use it as the norm. + git log -1 -p same >log-p.output && + git patch-id <log-p.output >expect && + + # format-patch begins with "From <commit object name>" + git format-patch -1 --stdout same >format-patch.output && + git patch-id <format-patch.output >actual && + test_cmp actual expect && + + # "diff-tree --stdin -p" begins with "<commit object name>" + same=$(git rev-parse same) && + echo $same | git diff-tree --stdin -p >diff-tree.output && + git patch-id <diff-tree.output >actual && + test_cmp actual expect && + + # "diff-tree --stdin -v -p" begins with "commit <commit object name>" + echo $same | git diff-tree --stdin -p -v >diff-tree-v.output && + git patch-id <diff-tree-v.output >actual && + test_cmp actual expect +' + +hash=$(git rev-parse same:) +for cruft in "$hash" "commit $hash is bad" "From $hash status" +do + test_expect_success "patch-id with <$cruft> in log message" ' + git format-patch -1 --stdout same >patch-0 && + git patch-id <patch-0 >expect && + + { + sed -e "/^$/q" patch-0 && + printf "random message\n%s\n\n" "$cruft" && + sed -e "1,/^$/d" patch-0 + } >patch-cruft && + git patch-id <patch-cruft >actual && + test_cmp actual expect + ' +done + test_expect_success 'whitespace is irrelevant in footer' ' get_patch_id main && git checkout same && diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index a2b4442660..2916c07e3c 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='commit graph' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-chunk.sh diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 42e77eb5a9..d64b40e408 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -5,6 +5,7 @@ test_description='git ls-remote' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh generate_references () { @@ -402,4 +403,17 @@ test_expect_success 'v0 clients can handle multiple symrefs' ' test_cmp expect actual ' +test_expect_success 'helper with refspec capability fails gracefully' ' + mkdir test-bin && + write_script test-bin/git-remote-foo <<-EOF && + echo import + echo refspec ${SQ}*:*${SQ} + EOF + ( + PATH="$PWD/test-bin:$PATH" && + export PATH && + test_must_fail nongit git ls-remote foo::bar + ) +' + test_done diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh index 25772c85c5..579872c258 100755 --- a/t/t5514-fetch-multiple.sh +++ b/t/t5514-fetch-multiple.sh @@ -5,6 +5,7 @@ test_description='fetch --all works correctly' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh setup_repository () { diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 47534f1062..1098cbd0a1 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -5,6 +5,7 @@ test_description='pulling into void' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh modify () { diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh index 14f7eced9a..bc2bada34c 100755 --- a/t/t5528-push-default.sh +++ b/t/t5528-push-default.sh @@ -4,6 +4,7 @@ test_description='check various push.default settings' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup bare remotes' ' diff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh index e8f6d233ff..7122af7fdb 100755 --- a/t/t5535-fetch-push-symref.sh +++ b/t/t5535-fetch-push-symref.sh @@ -2,6 +2,7 @@ test_description='avoiding conflicting update through symref aliasing' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t5543-atomic-push.sh b/t/t5543-atomic-push.sh index 04b47ad84a..479d103469 100755 --- a/t/t5543-atomic-push.sh +++ b/t/t5543-atomic-push.sh @@ -5,6 +5,7 @@ test_description='pushing to a repository using the atomic push option' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh mk_repo_pair () { diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh index bb35b87071..4aef99bc28 100755 --- a/t/t5564-http-proxy.sh +++ b/t/t5564-http-proxy.sh @@ -39,4 +39,59 @@ test_expect_success 'clone can prompt for proxy password' ' expect_askpass pass proxuser ' +start_socks() { + mkfifo socks_output && + { + "$PERL_PATH" "$TEST_DIRECTORY/socks4-proxy.pl" "$1" >socks_output & + echo $! > "$TRASH_DIRECTORY/socks.pid" + } && + read line <socks_output && + test "$line" = ready +} + +# The %30 tests that the correct amount of percent-encoding is applied to the +# proxy string passed to curl. +test_lazy_prereq SOCKS_PROXY ' + test_have_prereq PERL && + start_socks "$TRASH_DIRECTORY/%30.sock" +' + +test_atexit ' + test ! -e "$TRASH_DIRECTORY/socks.pid" || + kill "$(cat "$TRASH_DIRECTORY/socks.pid")" +' + +# The below tests morally ought to be gated on a prerequisite that Git is +# linked with a libcurl that supports Unix socket paths for proxies (7.84 or +# later), but this is not easy to test right now. Instead, we || the tests with +# this function. +old_libcurl_error() { + grep -Fx "fatal: libcurl 7.84 or later is required to support paths in proxy URLs" "$1" +} + +test_expect_success SOCKS_PROXY 'clone via Unix socket' ' + test_when_finished "rm -rf clone" && + test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && { + { + GIT_TRACE_CURL=$PWD/trace git clone "$HTTPD_URL/smart/repo.git" clone 2>err && + grep -i "SOCKS4 request granted" trace + } || + old_libcurl_error err + } +' + +test_expect_success 'Unix socket requires socks*:' - <<\EOT + ! git clone -c http.proxy=localhost/path https://example.com/repo.git 2>err && { + grep -Fx "fatal: Invalid proxy URL 'localhost/path': only SOCKS proxies support paths" err || + old_libcurl_error err + } +EOT + +test_expect_success 'Unix socket requires localhost' - <<\EOT + ! git clone -c http.proxy=socks4://127.0.0.1/path https://example.com/repo.git 2>err && { + grep -Fx "fatal: Invalid proxy URL 'socks4://127.0.0.1/path': host must be localhost if a path is present" err || + old_libcurl_error err + } +EOT + test_done diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index f9a9bf9503..c5f08b6799 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -4,6 +4,7 @@ test_description='test fetching over git protocol' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-git-daemon.sh diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh index 51744521f7..916e58c166 100755 --- a/t/t5572-pull-submodule.sh +++ b/t/t5572-pull-submodule.sh @@ -5,6 +5,7 @@ test_description='pull can handle submodules' GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-submodule-update.sh diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index 6f3e543977..2d337d7287 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -5,6 +5,7 @@ test_description='test git rev-list --cherry-pick -- file' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # A---B---D---F diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 44c726ea39..f96ea82e78 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -6,6 +6,7 @@ test_description='Merge base and parent list computation. ' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh M=1130000000 diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 79e0f19deb..05ed2510d9 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -14,6 +14,7 @@ test_description='test describe' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh check_describe () { diff --git a/t/t6133-pathspec-rev-dwim.sh b/t/t6133-pathspec-rev-dwim.sh index a290ffca0d..6dd4bbbf9f 100755 --- a/t/t6133-pathspec-rev-dwim.sh +++ b/t/t6133-pathspec-rev-dwim.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test dwim of revs versus pathspecs in revision parser' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t6421-merge-partial-clone.sh b/t/t6421-merge-partial-clone.sh index 711b709e75..b99f29ef9b 100755 --- a/t/t6421-merge-partial-clone.sh +++ b/t/t6421-merge-partial-clone.sh @@ -230,8 +230,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded for single relev grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual && test_cmp expect actual && - # Check the number of fetch commands exec-ed - grep d0.*fetch.negotiationAlgorithm trace.output >fetches && + # Check the number of fetch commands exec-ed by filtering trace to + # child_start events by the top-level program (2nd field == d0) + grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches && test_line_count = 2 fetches && git rev-list --objects --all --missing=print | @@ -318,8 +319,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded when a directory grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual && test_cmp expect actual && - # Check the number of fetch commands exec-ed - grep d0.*fetch.negotiationAlgorithm trace.output >fetches && + # Check the number of fetch commands exec-ed by filtering trace to + # child_start events by the top-level program (2nd field == d0) + grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches && test_line_count = 1 fetches && git rev-list --objects --all --missing=print | @@ -422,8 +424,9 @@ test_expect_merge_algorithm failure success 'Objects downloaded with lots of ren grep fetch_count trace.output | cut -d "|" -f 9 | tr -d " ." >actual && test_cmp expect actual && - # Check the number of fetch commands exec-ed - grep d0.*fetch.negotiationAlgorithm trace.output >fetches && + # Check the number of fetch commands exec-ed by filtering trace to + # child_start events by the top-level program (2nd field == d0) + grep " d0 .* child_start .*fetch.negotiationAlgorithm" trace.output >fetches && test_line_count = 4 fetches && git rev-list --objects --all --missing=print | diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index fa6336edf9..b1316e62f4 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -61,8 +61,9 @@ test_expect_success 'sort tags, ignore case' ' ) ' -test_expect_success 'looking for a tag in an empty tree should fail' \ - '! (tag_exists mytag)' +test_expect_success 'looking for a tag in an empty tree should fail' ' + ! (tag_exists mytag) +' test_expect_success 'creating a tag in an empty tree should fail' ' test_must_fail git tag mynotag && @@ -129,10 +130,10 @@ test_expect_success 'listing all tags if one exists should succeed' ' git tag ' -cat >expect <<EOF -mytag -EOF test_expect_success 'Multiple -l or --list options are equivalent to one -l option' ' + cat >expect <<-\EOF && + mytag + EOF git tag -l -l >actual && test_cmp expect actual && git tag --list --list >actual && @@ -148,32 +149,33 @@ test_expect_success 'listing all tags if one exists should output that tag' ' # pattern matching: -test_expect_success 'listing a tag using a matching pattern should succeed' \ - 'git tag -l mytag' +test_expect_success 'listing a tag using a matching pattern should succeed' ' + git tag -l mytag +' -test_expect_success 'listing a tag with --ignore-case' \ - 'test $(git tag -l --ignore-case MYTAG) = mytag' +test_expect_success 'listing a tag with --ignore-case' ' + test $(git tag -l --ignore-case MYTAG) = mytag +' -test_expect_success \ - 'listing a tag using a matching pattern should output that tag' \ - 'test $(git tag -l mytag) = mytag' +test_expect_success 'listing a tag using a matching pattern should output that tag' ' + test $(git tag -l mytag) = mytag +' -test_expect_success \ - 'listing tags using a non-matching pattern should succeed' \ - 'git tag -l xxx' +test_expect_success 'listing tags using a non-matching pattern should succeed' ' + git tag -l xxx +' -test_expect_success \ - 'listing tags using a non-matching pattern should output nothing' \ - 'test $(git tag -l xxx | wc -l) -eq 0' +test_expect_success 'listing tags using a non-matching pattern should output nothing' ' + test $(git tag -l xxx | wc -l) -eq 0 +' # special cases for creating tags: -test_expect_success \ - 'trying to create a tag with the name of one existing should fail' \ - 'test_must_fail git tag mytag' +test_expect_success 'trying to create a tag with the name of one existing should fail' ' + test_must_fail git tag mytag +' -test_expect_success \ - 'trying to create a tag with a non-valid name should fail' ' +test_expect_success 'trying to create a tag with a non-valid name should fail' ' test $(git tag -l | wc -l) -eq 1 && test_must_fail git tag "" && test_must_fail git tag .othertag && @@ -207,19 +209,19 @@ test_expect_success 'trying to delete an unknown tag should fail' ' test_must_fail git tag -d unknown-tag ' -cat >expect <<EOF -myhead -mytag -EOF -test_expect_success \ - 'trying to delete tags without params should succeed and do nothing' ' - git tag -l > actual && test_cmp expect actual && +test_expect_success 'trying to delete tags without params should succeed and do nothing' ' + cat >expect <<-\EOF && + myhead + mytag + EOF + git tag -l >actual && + test_cmp expect actual && git tag -d && - git tag -l > actual && test_cmp expect actual + git tag -l >actual && + test_cmp expect actual ' -test_expect_success \ - 'deleting two existing tags in one command should succeed' ' +test_expect_success 'deleting two existing tags in one command should succeed' ' tag_exists mytag && tag_exists myhead && git tag -d mytag myhead && @@ -227,15 +229,13 @@ test_expect_success \ ! tag_exists myhead ' -test_expect_success \ - 'creating a tag with the name of another deleted one should succeed' ' +test_expect_success 'creating a tag with the name of another deleted one should succeed' ' ! tag_exists mytag && git tag mytag && tag_exists mytag ' -test_expect_success \ - 'trying to delete two tags, existing and not, should fail in the 2nd' ' +test_expect_success 'trying to delete two tags, existing and not, should fail in the 2nd' ' tag_exists mytag && ! tag_exists nonexistingtag && test_must_fail git tag -d mytag nonexistingtag && @@ -243,23 +243,24 @@ test_expect_success \ ! tag_exists nonexistingtag ' -test_expect_success 'trying to delete an already deleted tag should fail' \ - 'test_must_fail git tag -d mytag' +test_expect_success 'trying to delete an already deleted tag should fail' ' + test_must_fail git tag -d mytag +' # listing various tags with pattern matching: -cat >expect <<EOF -a1 -aa1 -cba -t210 -t211 -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -EOF test_expect_success 'listing all tags should print them ordered' ' + cat >expect <<-\EOF && + a1 + aa1 + cba + t210 + t211 + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + EOF git tag v1.0.1 && git tag t211 && git tag aa1 && @@ -269,91 +270,89 @@ test_expect_success 'listing all tags should print them ordered' ' git tag a1 && git tag v1.0 && git tag t210 && - git tag -l > actual && + git tag -l >actual && test_cmp expect actual && - git tag > actual && + git tag >actual && test_cmp expect actual ' -cat >expect <<EOF -a1 -aa1 -cba -EOF -test_expect_success \ - 'listing tags with substring as pattern must print those matching' ' +test_expect_success 'listing tags with substring as pattern must print those matching' ' + cat >expect <<-\EOF && + a1 + aa1 + cba + EOF rm *a* && - git tag -l "*a*" > current && + git tag -l "*a*" >current && test_cmp expect current ' -cat >expect <<EOF -v0.2.1 -v1.0.1 -EOF -test_expect_success \ - 'listing tags with a suffix as pattern must print those matching' ' - git tag -l "*.1" > actual && +test_expect_success 'listing tags with a suffix as pattern must print those matching' ' + cat >expect <<-\EOF && + v0.2.1 + v1.0.1 + EOF + git tag -l "*.1" >actual && test_cmp expect actual ' -cat >expect <<EOF -t210 -t211 -EOF -test_expect_success \ - 'listing tags with a prefix as pattern must print those matching' ' - git tag -l "t21*" > actual && +test_expect_success 'listing tags with a prefix as pattern must print those matching' ' + cat >expect <<-\EOF && + t210 + t211 + EOF + git tag -l "t21*" >actual && test_cmp expect actual ' -cat >expect <<EOF -a1 -EOF -test_expect_success \ - 'listing tags using a name as pattern must print that one matching' ' - git tag -l a1 > actual && +test_expect_success 'listing tags using a name as pattern must print that one matching' ' + cat >expect <<-\EOF && + a1 + EOF + git tag -l a1 >actual && test_cmp expect actual ' -cat >expect <<EOF -v1.0 -EOF -test_expect_success \ - 'listing tags using a name as pattern must print that one matching' ' - git tag -l v1.0 > actual && +test_expect_success 'listing tags using a name as pattern must print that one matching' ' + cat >expect <<-\EOF && + v1.0 + EOF + git tag -l v1.0 >actual && test_cmp expect actual ' -cat >expect <<EOF -v1.0.1 -v1.1.3 -EOF -test_expect_success \ - 'listing tags with ? in the pattern should print those matching' ' - git tag -l "v1.?.?" > actual && +test_expect_success 'listing tags with ? in the pattern should print those matching' ' + cat >expect <<-\EOF && + v1.0.1 + v1.1.3 + EOF + git tag -l "v1.?.?" >actual && test_cmp expect actual ' -test_expect_success \ - 'listing tags using v.* should print nothing because none have v.' ' - git tag -l "v.*" > actual && +test_expect_success 'listing tags using v.* should print nothing because none have v.' ' + git tag -l "v.*" >actual && test_must_be_empty actual ' -cat >expect <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -EOF -test_expect_success \ - 'listing tags using v* should print only those having v' ' - git tag -l "v*" > actual && +test_expect_success 'listing tags using v* should print only those having v' ' + cat >expect <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + EOF + git tag -l "v*" >actual && test_cmp expect actual ' test_expect_success 'tag -l can accept multiple patterns' ' + cat >expect <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + EOF git tag -l "v1*" "v0*" >actual && test_cmp expect actual ' @@ -367,16 +366,22 @@ test_expect_success 'tag -l can accept multiple patterns' ' # out if we're going to break this long-documented form of taking # multiple patterns. test_expect_success 'tag -l <pattern> -l <pattern> works, as our buggy documentation previously suggested' ' + cat >expect <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + EOF git tag -l "v1*" -l "v0*" >actual && test_cmp expect actual ' test_expect_success 'listing tags in column' ' COLUMNS=41 git tag -l --column=row >actual && - cat >expected <<\EOF && -a1 aa1 cba t210 t211 -v0.2.1 v1.0 v1.0.1 v1.1.3 -EOF + cat >expected <<-\EOF && + a1 aa1 cba t210 t211 + v0.2.1 v1.0 v1.0.1 v1.1.3 + EOF test_cmp expected actual ' @@ -384,10 +389,10 @@ test_expect_success 'listing tags in column with column.*' ' test_config column.tag row && test_config column.ui dense && COLUMNS=40 git tag -l >actual && - cat >expected <<\EOF && -a1 aa1 cba t210 t211 -v0.2.1 v1.0 v1.0.1 v1.1.3 -EOF + cat >expected <<-\EOF && + a1 aa1 cba t210 t211 + v0.2.1 v1.0 v1.0.1 v1.1.3 + EOF test_cmp expected actual ' @@ -398,39 +403,39 @@ test_expect_success 'listing tag with -n --column should fail' ' test_expect_success 'listing tags -n in column with column.ui ignored' ' test_config column.ui "row dense" && COLUMNS=40 git tag -l -n >actual && - cat >expected <<\EOF && -a1 Foo -aa1 Foo -cba Foo -t210 Foo -t211 Foo -v0.2.1 Foo -v1.0 Foo -v1.0.1 Foo -v1.1.3 Foo -EOF + cat >expected <<-\EOF && + a1 Foo + aa1 Foo + cba Foo + t210 Foo + t211 Foo + v0.2.1 Foo + v1.0 Foo + v1.0.1 Foo + v1.1.3 Foo + EOF test_cmp expected actual ' # creating and verifying lightweight tags: -test_expect_success \ - 'a non-annotated tag created without parameters should point to HEAD' ' +test_expect_success 'a non-annotated tag created without parameters should point to HEAD' ' git tag non-annotated-tag && test $(git cat-file -t non-annotated-tag) = commit && test $(git rev-parse non-annotated-tag) = $(git rev-parse HEAD) ' -test_expect_success 'trying to verify an unknown tag should fail' \ - 'test_must_fail git tag -v unknown-tag' +test_expect_success 'trying to verify an unknown tag should fail' ' + test_must_fail git tag -v unknown-tag +' -test_expect_success \ - 'trying to verify a non-annotated and non-signed tag should fail' \ - 'test_must_fail git tag -v non-annotated-tag' +test_expect_success 'trying to verify a non-annotated and non-signed tag should fail' ' + test_must_fail git tag -v non-annotated-tag +' -test_expect_success \ - 'trying to verify many non-annotated or unknown tags, should fail' \ - 'test_must_fail git tag -v unknown-tag1 non-annotated-tag unknown-tag2' +test_expect_success 'trying to verify many non-annotated or unknown tags, should fail' ' + test_must_fail git tag -v unknown-tag1 non-annotated-tag unknown-tag2 +' # creating annotated tags: @@ -449,83 +454,78 @@ tagger C O Mitter <committer@example.com> $4 -0700 EOF } -commit=$(git rev-parse HEAD) -time=$test_tick - -get_tag_header annotated-tag $commit commit $time >expect -echo "A message" >>expect -test_expect_success \ - 'creating an annotated tag with -m message should succeed' ' +test_expect_success 'creating an annotated tag with -m message should succeed' ' + commit=$(git rev-parse HEAD) && + time=$test_tick && + get_tag_header annotated-tag $commit commit $time >expect && + echo "A message" >>expect && git tag -m "A message" annotated-tag && get_tag_msg annotated-tag >actual && test_cmp expect actual ' -get_tag_header annotated-tag-edit $commit commit $time >expect -echo "An edited message" >>expect test_expect_success 'set up editor' ' write_script fakeeditor <<-\EOF sed -e "s/A message/An edited message/g" <"$1" >"$1-" mv "$1-" "$1" EOF ' -test_expect_success \ - 'creating an annotated tag with -m message --edit should succeed' ' + +test_expect_success 'creating an annotated tag with -m message --edit should succeed' ' + get_tag_header annotated-tag-edit $commit commit $time >expect && + echo "An edited message" >>expect && GIT_EDITOR=./fakeeditor git tag -m "A message" --edit annotated-tag-edit && get_tag_msg annotated-tag-edit >actual && test_cmp expect actual ' -cat >msgfile <<EOF -Another message -in a file. -EOF -get_tag_header file-annotated-tag $commit commit $time >expect -cat msgfile >>expect -test_expect_success \ - 'creating an annotated tag with -F messagefile should succeed' ' +test_expect_success 'creating an annotated tag with -F messagefile should succeed' ' + cat >msgfile <<-\EOF && + Another message + in a file. + EOF + get_tag_header file-annotated-tag $commit commit $time >expect && + cat msgfile >>expect && git tag -F msgfile file-annotated-tag && get_tag_msg file-annotated-tag >actual && test_cmp expect actual ' -get_tag_header file-annotated-tag-edit $commit commit $time >expect -sed -e "s/Another message/Another edited message/g" msgfile >>expect test_expect_success 'set up editor' ' write_script fakeeditor <<-\EOF sed -e "s/Another message/Another edited message/g" <"$1" >"$1-" mv "$1-" "$1" EOF ' -test_expect_success \ - 'creating an annotated tag with -F messagefile --edit should succeed' ' + +test_expect_success 'creating an annotated tag with -F messagefile --edit should succeed' ' + get_tag_header file-annotated-tag-edit $commit commit $time >expect && + sed -e "s/Another message/Another edited message/g" msgfile >>expect && GIT_EDITOR=./fakeeditor git tag -F msgfile --edit file-annotated-tag-edit && get_tag_msg file-annotated-tag-edit >actual && test_cmp expect actual ' -cat >inputmsg <<EOF -A message from the -standard input -EOF -get_tag_header stdin-annotated-tag $commit commit $time >expect -cat inputmsg >>expect test_expect_success 'creating an annotated tag with -F - should succeed' ' + cat >inputmsg <<-\EOF && + A message from the + standard input + EOF + get_tag_header stdin-annotated-tag $commit commit $time >expect && + cat inputmsg >>expect && git tag -F - stdin-annotated-tag <inputmsg && get_tag_msg stdin-annotated-tag >actual && test_cmp expect actual ' -test_expect_success \ - 'trying to create a tag with a non-existing -F file should fail' ' +test_expect_success 'trying to create a tag with a non-existing -F file should fail' ' ! test -f nonexistingfile && ! tag_exists notag && test_must_fail git tag -F nonexistingfile notag && ! tag_exists notag ' -test_expect_success \ - 'trying to create tags giving both -m or -F options should fail' ' +test_expect_success 'trying to create tags giving both -m or -F options should fail' ' echo "message file 1" >msgfile1 && ! tag_exists msgtag && test_must_fail git tag -m "message 1" -F msgfile1 msgtag && @@ -539,67 +539,61 @@ test_expect_success \ # blank and empty messages: -get_tag_header empty-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with an empty -m message should succeed' ' +test_expect_success 'creating a tag with an empty -m message should succeed' ' + get_tag_header empty-annotated-tag $commit commit $time >expect && git tag -m "" empty-annotated-tag && get_tag_msg empty-annotated-tag >actual && test_cmp expect actual ' ->emptyfile -get_tag_header emptyfile-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with an empty -F messagefile should succeed' ' +test_expect_success 'creating a tag with an empty -F messagefile should succeed' ' + >emptyfile && + get_tag_header emptyfile-annotated-tag $commit commit $time >expect && git tag -F emptyfile emptyfile-annotated-tag && get_tag_msg emptyfile-annotated-tag >actual && test_cmp expect actual ' -printf '\n\n \n\t\nLeading blank lines\n' >blanksfile -printf '\n\t \t \nRepeated blank lines\n' >>blanksfile -printf '\n\n\nTrailing spaces \t \n' >>blanksfile -printf '\nTrailing blank lines\n\n\t \n\n' >>blanksfile -get_tag_header blanks-annotated-tag $commit commit $time >expect -cat >>expect <<EOF -Leading blank lines +test_expect_success 'extra blanks in the message for an annotated tag should be removed' ' + printf "\n\n \n\t\nLeading blank lines\n" >blanksfile && + printf "\n\t \t \nRepeated blank lines\n" >>blanksfile && + printf "\n\n\nTrailing spaces \t \n" >>blanksfile && + printf "\nTrailing blank lines\n\n\t \n\n" >>blanksfile && + get_tag_header blanks-annotated-tag $commit commit $time >expect && + cat >>expect <<-\EOF && + Leading blank lines -Repeated blank lines + Repeated blank lines -Trailing spaces + Trailing spaces -Trailing blank lines -EOF -test_expect_success \ - 'extra blanks in the message for an annotated tag should be removed' ' + Trailing blank lines + EOF git tag -F blanksfile blanks-annotated-tag && get_tag_msg blanks-annotated-tag >actual && test_cmp expect actual ' -get_tag_header blank-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with blank -m message with spaces should succeed' ' +test_expect_success 'creating a tag with blank -m message with spaces should succeed' ' + get_tag_header blank-annotated-tag $commit commit $time >expect && git tag -m " " blank-annotated-tag && get_tag_msg blank-annotated-tag >actual && test_cmp expect actual ' -echo ' ' >blankfile -echo '' >>blankfile -echo ' ' >>blankfile -get_tag_header blankfile-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with blank -F messagefile with spaces should succeed' ' +test_expect_success 'creating a tag with blank -F messagefile with spaces should succeed' ' + echo " " >blankfile && + echo "" >>blankfile && + echo " " >>blankfile && + get_tag_header blankfile-annotated-tag $commit commit $time >expect && git tag -F blankfile blankfile-annotated-tag && get_tag_msg blankfile-annotated-tag >actual && test_cmp expect actual ' -printf ' ' >blanknonlfile -get_tag_header blanknonlfile-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with -F file of spaces and no newline should succeed' ' +test_expect_success 'creating a tag with -F file of spaces and no newline should succeed' ' + printf " " >blanknonlfile && + get_tag_header blanknonlfile-annotated-tag $commit commit $time >expect && git tag -F blanknonlfile blanknonlfile-annotated-tag && get_tag_msg blanknonlfile-annotated-tag >actual && test_cmp expect actual @@ -607,62 +601,58 @@ test_expect_success \ # messages with commented lines: -cat >commentsfile <<EOF -# A comment +test_expect_success 'creating a tag using a -F messagefile with #comments should succeed' ' + cat >commentsfile <<-\EOF && + # A comment -############ -The message. -############ -One line. + ############ + The message. + ############ + One line. -# commented lines -# commented lines + # commented lines + # commented lines -Another line. -# comments + Another line. + # comments -Last line. -EOF -get_tag_header comments-annotated-tag $commit commit $time >expect -cat >>expect <<EOF -The message. -One line. + Last line. + EOF + get_tag_header comments-annotated-tag $commit commit $time >expect && + cat >>expect <<-\EOF && + The message. + One line. -Another line. + Another line. -Last line. -EOF -test_expect_success \ - 'creating a tag using a -F messagefile with #comments should succeed' ' + Last line. + EOF git tag -F commentsfile comments-annotated-tag && get_tag_msg comments-annotated-tag >actual && test_cmp expect actual ' -get_tag_header comment-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with a #comment in the -m message should succeed' ' +test_expect_success 'creating a tag with a #comment in the -m message should succeed' ' + get_tag_header comment-annotated-tag $commit commit $time >expect && git tag -m "#comment" comment-annotated-tag && get_tag_msg comment-annotated-tag >actual && test_cmp expect actual ' -echo '#comment' >commentfile -echo '' >>commentfile -echo '####' >>commentfile -get_tag_header commentfile-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with #comments in the -F messagefile should succeed' ' +test_expect_success 'creating a tag with #comments in the -F messagefile should succeed' ' + echo "#comment" >commentfile && + echo "" >>commentfile && + echo "####" >>commentfile && + get_tag_header commentfile-annotated-tag $commit commit $time >expect && git tag -F commentfile commentfile-annotated-tag && get_tag_msg commentfile-annotated-tag >actual && test_cmp expect actual ' -printf '#comment' >commentnonlfile -get_tag_header commentnonlfile-annotated-tag $commit commit $time >expect -test_expect_success \ - 'creating a tag with a file of #comment and no newline should succeed' ' +test_expect_success 'creating a tag with a file of #comment and no newline should succeed' ' + printf "#comment" >commentnonlfile && + get_tag_header commentnonlfile-annotated-tag $commit commit $time >expect && git tag -F commentnonlfile commentnonlfile-annotated-tag && get_tag_msg commentnonlfile-annotated-tag >actual && test_cmp expect actual @@ -779,8 +769,7 @@ test_expect_success 'bad editor causes panic when only --trailer is given' ' # listing messages for annotated non-signed tags: -test_expect_success \ - 'listing the one-line message of a non-signed tag should succeed' ' +test_expect_success 'listing the one-line message of a non-signed tag should succeed' ' git tag -m "A msg" tag-one-line && echo "tag-one-line" >expect && @@ -819,8 +808,7 @@ test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' ' test_cmp expect actual ' -test_expect_success \ - 'listing the zero-lines message of a non-signed tag should succeed' ' +test_expect_success 'listing the zero-lines message of a non-signed tag should succeed' ' git tag -m "" tag-zero-lines && echo "tag-zero-lines" >expect && @@ -844,11 +832,10 @@ test_expect_success \ test_cmp expect actual ' -echo 'tag line one' >annotagmsg -echo 'tag line two' >>annotagmsg -echo 'tag line three' >>annotagmsg -test_expect_success \ - 'listing many message lines of a non-signed tag should succeed' ' +test_expect_success 'listing many message lines of a non-signed tag should succeed' ' + echo "tag line one" >annotagmsg && + echo "tag line two" >>annotagmsg && + echo "tag line three" >>annotagmsg && git tag -F annotagmsg tag-lines && echo "tag-lines" >expect && @@ -936,40 +923,37 @@ test_expect_success 'git tag --format with ahead-behind' ' # trying to verify annotated non-signed tags: -test_expect_success GPG \ - 'trying to verify an annotated non-signed tag should fail' ' +test_expect_success GPG 'trying to verify an annotated non-signed tag should fail' ' tag_exists annotated-tag && test_must_fail git tag -v annotated-tag ' -test_expect_success GPG \ - 'trying to verify a file-annotated non-signed tag should fail' ' +test_expect_success GPG 'trying to verify a file-annotated non-signed tag should fail' ' tag_exists file-annotated-tag && test_must_fail git tag -v file-annotated-tag ' -test_expect_success GPG \ - 'trying to verify two annotated non-signed tags should fail' ' +test_expect_success GPG 'trying to verify two annotated non-signed tags should fail' ' tag_exists annotated-tag file-annotated-tag && test_must_fail git tag -v annotated-tag file-annotated-tag ' # creating and verifying signed tags: -get_tag_header signed-tag $commit commit $time >expect -echo 'A signed tag message' >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success GPG 'creating a signed tag with -m message should succeed' ' + get_tag_header signed-tag $commit commit $time >expect && + echo "A signed tag message" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m "A signed tag message" signed-tag && get_tag_msg signed-tag >actual && test_cmp expect actual ' -get_tag_header u-signed-tag $commit commit $time >expect -echo 'Another message' >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success GPG 'sign with a given key id' ' + get_tag_header u-signed-tag $commit commit $time >expect && + echo "Another message" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -u committer@example.com -m "Another message" u-signed-tag && get_tag_msg u-signed-tag >actual && test_cmp expect actual @@ -989,137 +973,128 @@ test_expect_success GPG 'sign with an unknown id (2)' ' ' -cat >fakeeditor <<'EOF' -#!/bin/sh -test -n "$1" && exec >"$1" -echo A signed tag message -echo from a fake editor. -EOF -chmod +x fakeeditor - -get_tag_header implied-sign $commit commit $time >expect -./fakeeditor >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success GPG '-u implies signed tag' ' + write_script fakeeditor <<-\EOF && + test -n "$1" && exec >"$1" + echo A signed tag message + echo from a fake editor. + EOF + + get_tag_header implied-sign $commit commit $time >expect && + ./fakeeditor >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && GIT_EDITOR=./fakeeditor git tag -u CDDE430D implied-sign && get_tag_msg implied-sign >actual && test_cmp expect actual ' -cat >sigmsgfile <<EOF -Another signed tag -message in a file. -EOF -get_tag_header file-signed-tag $commit commit $time >expect -cat sigmsgfile >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with -F messagefile should succeed' ' +test_expect_success GPG 'creating a signed tag with -F messagefile should succeed' ' + cat >sigmsgfile <<-\EOF && + Another signed tag + message in a file. + EOF + get_tag_header file-signed-tag $commit commit $time >expect && + cat sigmsgfile >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigmsgfile file-signed-tag && get_tag_msg file-signed-tag >actual && test_cmp expect actual ' -cat >siginputmsg <<EOF -A signed tag message from -the standard input -EOF -get_tag_header stdin-signed-tag $commit commit $time >expect -cat siginputmsg >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success GPG 'creating a signed tag with -F - should succeed' ' + cat >siginputmsg <<-\EOF && + A signed tag message from + the standard input + EOF + get_tag_header stdin-signed-tag $commit commit $time >expect && + cat siginputmsg >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F - stdin-signed-tag <siginputmsg && get_tag_msg stdin-signed-tag >actual && test_cmp expect actual ' -get_tag_header implied-annotate $commit commit $time >expect -./fakeeditor >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect test_expect_success GPG '-s implies annotated tag' ' + get_tag_header implied-annotate $commit commit $time >expect && + ./fakeeditor >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && GIT_EDITOR=./fakeeditor git tag -s implied-annotate && get_tag_msg implied-annotate >actual && test_cmp expect actual ' -get_tag_header forcesignannotated-implied-sign $commit commit $time >expect -echo "A message" >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'git tag -s implied if configured with tag.forcesignannotated' \ - 'test_config tag.forcesignannotated true && +test_expect_success GPG 'git tag -s implied if configured with tag.forcesignannotated' ' + get_tag_header forcesignannotated-implied-sign $commit commit $time >expect && + echo "A message" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && + test_config tag.forcesignannotated true && git tag -m "A message" forcesignannotated-implied-sign && get_tag_msg forcesignannotated-implied-sign >actual && test_cmp expect actual ' -test_expect_success GPG \ - 'lightweight with no message when configured with tag.forcesignannotated' \ - 'test_config tag.forcesignannotated true && +test_expect_success GPG 'lightweight with no message when configured with tag.forcesignannotated' ' + test_config tag.forcesignannotated true && git tag forcesignannotated-lightweight && tag_exists forcesignannotated-lightweight && test_must_fail git tag -v forcesignannotated-no-message ' -get_tag_header forcesignannotated-annotate $commit commit $time >expect -echo "A message" >>expect -test_expect_success GPG \ - 'git tag -a disable configured tag.forcesignannotated' \ - 'test_config tag.forcesignannotated true && +test_expect_success GPG 'git tag -a disable configured tag.forcesignannotated' ' + get_tag_header forcesignannotated-annotate $commit commit $time >expect && + echo "A message" >>expect && + test_config tag.forcesignannotated true && git tag -a -m "A message" forcesignannotated-annotate && get_tag_msg forcesignannotated-annotate >actual && test_cmp expect actual && test_must_fail git tag -v forcesignannotated-annotate ' -get_tag_header forcesignannotated-disabled $commit commit $time >expect -echo "A message" >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'git tag --sign enable GPG sign' \ - 'test_config tag.forcesignannotated false && +test_expect_success GPG 'git tag --sign enable GPG sign' ' + get_tag_header forcesignannotated-disabled $commit commit $time >expect && + echo "A message" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && + test_config tag.forcesignannotated false && git tag --sign -m "A message" forcesignannotated-disabled && get_tag_msg forcesignannotated-disabled >actual && test_cmp expect actual ' -get_tag_header gpgsign-enabled $commit commit $time >expect -echo "A message" >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'git tag configured tag.gpgsign enables GPG sign' \ - 'test_config tag.gpgsign true && +test_expect_success GPG 'git tag configured tag.gpgsign enables GPG sign' ' + get_tag_header gpgsign-enabled $commit commit $time >expect && + echo "A message" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && + test_config tag.gpgsign true && git tag -m "A message" gpgsign-enabled && get_tag_msg gpgsign-enabled>actual && test_cmp expect actual ' -get_tag_header no-sign $commit commit $time >expect -echo "A message" >>expect -test_expect_success GPG \ - 'git tag --no-sign configured tag.gpgsign skip GPG sign' \ - 'test_config tag.gpgsign true && +test_expect_success GPG 'git tag --no-sign configured tag.gpgsign skip GPG sign' ' + get_tag_header no-sign $commit commit $time >expect && + echo "A message" >>expect && + test_config tag.gpgsign true && git tag -a --no-sign -m "A message" no-sign && get_tag_msg no-sign>actual && test_cmp expect actual ' -test_expect_success GPG \ - 'trying to create a signed tag with non-existing -F file should fail' ' +test_expect_success GPG 'trying to create a signed tag with non-existing -F file should fail' ' ! test -f nonexistingfile && ! tag_exists nosigtag && test_must_fail git tag -s -F nonexistingfile nosigtag && ! tag_exists nosigtag ' -test_expect_success GPG 'verifying a signed tag should succeed' \ - 'git tag -v signed-tag' +test_expect_success GPG 'verifying a signed tag should succeed' ' + git tag -v signed-tag +' -test_expect_success GPG 'verifying two signed tags in one command should succeed' \ - 'git tag -v signed-tag file-signed-tag' +test_expect_success GPG 'verifying two signed tags in one command should succeed' ' + git tag -v signed-tag file-signed-tag +' -test_expect_success GPG \ - 'verifying many signed and non-signed tags should fail' ' +test_expect_success GPG 'verifying many signed and non-signed tags should fail' ' test_must_fail git tag -v signed-tag annotated-tag && test_must_fail git tag -v file-annotated-tag file-signed-tag && test_must_fail git tag -v annotated-tag \ @@ -1150,78 +1125,72 @@ test_expect_success GPG 'verifying a forged tag with --format should fail silent # blank and empty messages for signed tags: -get_tag_header empty-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with an empty -m message should succeed' ' +test_expect_success GPG 'creating a signed tag with an empty -m message should succeed' ' + get_tag_header empty-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m "" empty-signed-tag && get_tag_msg empty-signed-tag >actual && test_cmp expect actual && git tag -v empty-signed-tag ' ->sigemptyfile -get_tag_header emptyfile-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with an empty -F messagefile should succeed' ' +test_expect_success GPG 'creating a signed tag with an empty -F messagefile should succeed' ' + >sigemptyfile && + get_tag_header emptyfile-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigemptyfile emptyfile-signed-tag && get_tag_msg emptyfile-signed-tag >actual && test_cmp expect actual && git tag -v emptyfile-signed-tag ' -printf '\n\n \n\t\nLeading blank lines\n' > sigblanksfile -printf '\n\t \t \nRepeated blank lines\n' >>sigblanksfile -printf '\n\n\nTrailing spaces \t \n' >>sigblanksfile -printf '\nTrailing blank lines\n\n\t \n\n' >>sigblanksfile -get_tag_header blanks-signed-tag $commit commit $time >expect -cat >>expect <<EOF -Leading blank lines +test_expect_success GPG 'extra blanks in the message for a signed tag should be removed' ' + printf "\n\n \n\t\nLeading blank lines\n" >sigblanksfile && + printf "\n\t \t \nRepeated blank lines\n" >>sigblanksfile && + printf "\n\n\nTrailing spaces \t \n" >>sigblanksfile && + printf "\nTrailing blank lines\n\n\t \n\n" >>sigblanksfile && + get_tag_header blanks-signed-tag $commit commit $time >expect && + cat >>expect <<-\EOF && + Leading blank lines -Repeated blank lines + Repeated blank lines -Trailing spaces + Trailing spaces -Trailing blank lines -EOF -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'extra blanks in the message for a signed tag should be removed' ' + Trailing blank lines + EOF + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigblanksfile blanks-signed-tag && get_tag_msg blanks-signed-tag >actual && test_cmp expect actual && git tag -v blanks-signed-tag ' -get_tag_header blank-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with a blank -m message should succeed' ' +test_expect_success GPG 'creating a signed tag with a blank -m message should succeed' ' + get_tag_header blank-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m " " blank-signed-tag && get_tag_msg blank-signed-tag >actual && test_cmp expect actual && git tag -v blank-signed-tag ' -echo ' ' >sigblankfile -echo '' >>sigblankfile -echo ' ' >>sigblankfile -get_tag_header blankfile-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with blank -F file with spaces should succeed' ' +test_expect_success GPG 'creating a signed tag with blank -F file with spaces should succeed' ' + echo " " >sigblankfile && + echo "" >>sigblankfile && + echo " " >>sigblankfile && + get_tag_header blankfile-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigblankfile blankfile-signed-tag && get_tag_msg blankfile-signed-tag >actual && test_cmp expect actual && git tag -v blankfile-signed-tag ' -printf ' ' >sigblanknonlfile -get_tag_header blanknonlfile-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with spaces and no newline should succeed' ' +test_expect_success GPG 'creating a signed tag with spaces and no newline should succeed' ' + printf " " >sigblanknonlfile && + get_tag_header blanknonlfile-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigblanknonlfile blanknonlfile-signed-tag && get_tag_msg blanknonlfile-signed-tag >actual && test_cmp expect actual && @@ -1241,69 +1210,65 @@ test_expect_success GPG 'signed tag with embedded PGP message' ' # messages with commented lines for signed tags: -cat >sigcommentsfile <<EOF -# A comment +test_expect_success GPG 'creating a signed tag with a -F file with #comments should succeed' ' + cat >sigcommentsfile <<-\EOF && + # A comment -############ -The message. -############ -One line. + ############ + The message. + ############ + One line. -# commented lines -# commented lines + # commented lines + # commented lines -Another line. -# comments + Another line. + # comments -Last line. -EOF -get_tag_header comments-signed-tag $commit commit $time >expect -cat >>expect <<EOF -The message. -One line. + Last line. + EOF + get_tag_header comments-signed-tag $commit commit $time >expect && + cat >>expect <<-\EOF && + The message. + One line. -Another line. + Another line. -Last line. -EOF -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with a -F file with #comments should succeed' ' + Last line. + EOF + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigcommentsfile comments-signed-tag && get_tag_msg comments-signed-tag >actual && test_cmp expect actual && git tag -v comments-signed-tag ' -get_tag_header comment-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with #commented -m message should succeed' ' +test_expect_success GPG 'creating a signed tag with #commented -m message should succeed' ' + get_tag_header comment-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m "#comment" comment-signed-tag && get_tag_msg comment-signed-tag >actual && test_cmp expect actual && git tag -v comment-signed-tag ' -echo '#comment' >sigcommentfile -echo '' >>sigcommentfile -echo '####' >>sigcommentfile -get_tag_header commentfile-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with #commented -F messagefile should succeed' ' +test_expect_success GPG 'creating a signed tag with #commented -F messagefile should succeed' ' + echo "#comment" >sigcommentfile && + echo "" >>sigcommentfile && + echo "####" >>sigcommentfile && + get_tag_header commentfile-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigcommentfile commentfile-signed-tag && get_tag_msg commentfile-signed-tag >actual && test_cmp expect actual && git tag -v commentfile-signed-tag ' -printf '#comment' >sigcommentnonlfile -get_tag_header commentnonlfile-signed-tag $commit commit $time >expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag with a #comment and no newline should succeed' ' +test_expect_success GPG 'creating a signed tag with a #comment and no newline should succeed' ' + printf "#comment" >sigcommentnonlfile && + get_tag_header commentnonlfile-signed-tag $commit commit $time >expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -F sigcommentnonlfile commentnonlfile-signed-tag && get_tag_msg commentnonlfile-signed-tag >actual && test_cmp expect actual && @@ -1312,8 +1277,7 @@ test_expect_success GPG \ # listing messages for signed tags: -test_expect_success GPG \ - 'listing the one-line message of a signed tag should succeed' ' +test_expect_success GPG 'listing the one-line message of a signed tag should succeed' ' git tag -s -m "A message line signed" stag-one-line && echo "stag-one-line" >expect && @@ -1337,8 +1301,7 @@ test_expect_success GPG \ test_cmp expect actual ' -test_expect_success GPG \ - 'listing the zero-lines message of a signed tag should succeed' ' +test_expect_success GPG 'listing the zero-lines message of a signed tag should succeed' ' git tag -s -m "" stag-zero-lines && echo "stag-zero-lines" >expect && @@ -1362,11 +1325,10 @@ test_expect_success GPG \ test_cmp expect actual ' -echo 'stag line one' >sigtagmsg -echo 'stag line two' >>sigtagmsg -echo 'stag line three' >>sigtagmsg -test_expect_success GPG \ - 'listing many message lines of a signed tag should succeed' ' +test_expect_success GPG 'listing many message lines of a signed tag should succeed' ' + echo "stag line one" >sigtagmsg && + echo "stag line two" >>sigtagmsg && + echo "stag line three" >>sigtagmsg && git tag -s -F sigtagmsg stag-lines && echo "stag-lines" >expect && @@ -1408,74 +1370,64 @@ test_expect_success GPG \ # tags pointing to objects different from commits: -tree=$(git rev-parse HEAD^{tree}) -blob=$(git rev-parse HEAD:foo) -tag=$(git rev-parse signed-tag 2>/dev/null) - -get_tag_header tree-signed-tag $tree tree $time >expect -echo "A message for a tree" >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag pointing to a tree should succeed' ' +test_expect_success GPG 'creating a signed tag pointing to a tree should succeed' ' + tree=$(git rev-parse HEAD^{tree}) && + get_tag_header tree-signed-tag $tree tree $time >expect && + echo "A message for a tree" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} && get_tag_msg tree-signed-tag >actual && test_cmp expect actual ' -get_tag_header blob-signed-tag $blob blob $time >expect -echo "A message for a blob" >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag pointing to a blob should succeed' ' +test_expect_success GPG 'creating a signed tag pointing to a blob should succeed' ' + blob=$(git rev-parse HEAD:foo) && + get_tag_header blob-signed-tag $blob blob $time >expect && + echo "A message for a blob" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m "A message for a blob" blob-signed-tag HEAD:foo && get_tag_msg blob-signed-tag >actual && test_cmp expect actual ' -get_tag_header tag-signed-tag $tag tag $time >expect -echo "A message for another tag" >>expect -echo '-----BEGIN PGP SIGNATURE-----' >>expect -test_expect_success GPG \ - 'creating a signed tag pointing to another tag should succeed' ' +test_expect_success GPG 'creating a signed tag pointing to another tag should succeed' ' + tag=$(git rev-parse signed-tag 2>/dev/null) && + get_tag_header tag-signed-tag $tag tag $time >expect && + echo "A message for another tag" >>expect && + echo "-----BEGIN PGP SIGNATURE-----" >>expect && git tag -s -m "A message for another tag" tag-signed-tag signed-tag && get_tag_msg tag-signed-tag >actual && test_cmp expect actual ' # usage with rfc1991 signatures -get_tag_header rfc1991-signed-tag $commit commit $time >expect -echo "RFC1991 signed tag" >>expect -echo '-----BEGIN PGP MESSAGE-----' >>expect -test_expect_success GPG,RFC1991 \ - 'creating a signed tag with rfc1991' ' + +test_expect_success GPG,RFC1991 'creating a signed tag with rfc1991' ' + get_tag_header rfc1991-signed-tag $commit commit $time >expect && + echo "RFC1991 signed tag" >>expect && + echo "-----BEGIN PGP MESSAGE-----" >>expect && echo "rfc1991" >gpghome/gpg.conf && git tag -s -m "RFC1991 signed tag" rfc1991-signed-tag $commit && get_tag_msg rfc1991-signed-tag >actual && test_cmp expect actual ' -cat >fakeeditor <<'EOF' -#!/bin/sh -cp "$1" actual -EOF -chmod +x fakeeditor - -test_expect_success GPG,RFC1991 \ - 'reediting a signed tag body omits signature' ' +test_expect_success GPG,RFC1991 'reediting a signed tag body omits signature' ' + write_script fakeeditor <<-\EOF && + cp "$1" actual + EOF echo "rfc1991" >gpghome/gpg.conf && echo "RFC1991 signed tag" >expect && GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit && test_cmp expect actual ' -test_expect_success GPG,RFC1991 \ - 'verifying rfc1991 signature' ' +test_expect_success GPG,RFC1991 'verifying rfc1991 signature' ' echo "rfc1991" >gpghome/gpg.conf && git tag -v rfc1991-signed-tag ' -test_expect_success GPG,RFC1991 \ - 'list tag with rfc1991 signature' ' +test_expect_success GPG,RFC1991 'list tag with rfc1991 signature' ' echo "rfc1991" >gpghome/gpg.conf && echo "rfc1991-signed-tag RFC1991 signed tag" >expect && git tag -l -n1 rfc1991-signed-tag >actual && @@ -1486,15 +1438,12 @@ test_expect_success GPG,RFC1991 \ test_cmp expect actual ' -rm -f gpghome/gpg.conf - -test_expect_success GPG,RFC1991 \ - 'verifying rfc1991 signature without --rfc1991' ' +test_expect_success GPG,RFC1991 'verifying rfc1991 signature without --rfc1991' ' + rm -f gpghome/gpg.conf && git tag -v rfc1991-signed-tag ' -test_expect_success GPG,RFC1991 \ - 'list tag with rfc1991 signature without --rfc1991' ' +test_expect_success GPG,RFC1991 'list tag with rfc1991 signature without --rfc1991' ' echo "rfc1991-signed-tag RFC1991 signed tag" >expect && git tag -l -n1 rfc1991-signed-tag >actual && test_cmp expect actual && @@ -1504,24 +1453,23 @@ test_expect_success GPG,RFC1991 \ test_cmp expect actual ' -test_expect_success GPG,RFC1991 \ - 'reediting a signed tag body omits signature' ' +test_expect_success GPG,RFC1991 'reediting a signed tag body omits signature' ' echo "RFC1991 signed tag" >expect && GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit && test_cmp expect actual ' # try to sign with bad user.signingkey -test_expect_success GPG \ - 'git tag -s fails if gpg is misconfigured (bad key)' \ - 'test_config user.signingkey BobTheMouse && - test_must_fail git tag -s -m tail tag-gpg-failure' +test_expect_success GPG 'git tag -s fails if gpg is misconfigured (bad key)' ' + test_config user.signingkey BobTheMouse && + test_must_fail git tag -s -m tail tag-gpg-failure +' # try to produce invalid signature -test_expect_success GPG \ - 'git tag -s fails if gpg is misconfigured (bad signature format)' \ - 'test_config gpg.program echo && - test_must_fail git tag -s -m tail tag-gpg-failure' +test_expect_success GPG 'git tag -s fails if gpg is misconfigured (bad signature format)' ' + test_config gpg.program echo && + test_must_fail git tag -s -m tail tag-gpg-failure +' # try to produce invalid signature test_expect_success GPG 'git verifies tag is valid with double signature' ' @@ -1542,34 +1490,32 @@ test_expect_success GPG 'git verifies tag is valid with double signature' ' ' # try to sign with bad user.signingkey -test_expect_success GPGSM \ - 'git tag -s fails if gpgsm is misconfigured (bad key)' \ - 'test_config user.signingkey BobTheMouse && - test_config gpg.format x509 && - test_must_fail git tag -s -m tail tag-gpg-failure' +test_expect_success GPGSM 'git tag -s fails if gpgsm is misconfigured (bad key)' ' + test_config user.signingkey BobTheMouse && + test_config gpg.format x509 && + test_must_fail git tag -s -m tail tag-gpg-failure +' # try to produce invalid signature -test_expect_success GPGSM \ - 'git tag -s fails if gpgsm is misconfigured (bad signature format)' \ - 'test_config gpg.x509.program echo && - test_config gpg.format x509 && - test_must_fail git tag -s -m tail tag-gpg-failure' +test_expect_success GPGSM 'git tag -s fails if gpgsm is misconfigured (bad signature format)' ' + test_config gpg.x509.program echo && + test_config gpg.format x509 && + test_must_fail git tag -s -m tail tag-gpg-failure +' # try to verify without gpg: -rm -rf gpghome -test_expect_success GPG \ - 'verify signed tag fails when public key is not present' \ - 'test_must_fail git tag -v signed-tag' +test_expect_success GPG 'verify signed tag fails when public key is not present' ' + rm -rf gpghome && + test_must_fail git tag -v signed-tag +' -test_expect_success \ - 'git tag -a fails if tag annotation is empty' ' +test_expect_success 'git tag -a fails if tag annotation is empty' ' ! (GIT_EDITOR=cat git tag -a initial-comment) ' -test_expect_success \ - 'message in editor has initial comment' ' - ! (GIT_EDITOR=cat git tag -a initial-comment > actual) +test_expect_success 'message in editor has initial comment' ' + ! (GIT_EDITOR=cat git tag -a initial-comment >actual) ' test_expect_success 'message in editor has initial comment: first line' ' @@ -1579,17 +1525,15 @@ test_expect_success 'message in editor has initial comment: first line' ' test_cmp first.expect first.actual ' -test_expect_success \ - 'message in editor has initial comment: remainder' ' +test_expect_success 'message in editor has initial comment: remainder' ' # remove commented lines from the remainder -- should be empty sed -e 1d -e "/^#/d" <actual >rest.actual && test_must_be_empty rest.actual ' -get_tag_header reuse $commit commit $time >expect -echo "An annotation to be reused" >> expect -test_expect_success \ - 'overwriting an annotated tag should use its previous body' ' +test_expect_success 'overwriting an annotated tag should use its previous body' ' + get_tag_header reuse $commit commit $time >expect && + echo "An annotation to be reused" >>expect && git tag -a -m "An annotation to be reused" reuse && GIT_EDITOR=true git tag -f -a reuse && get_tag_msg reuse >actual && @@ -1618,192 +1562,202 @@ test_expect_success 'filename for the message is relative to cwd' ' # create a few more commits to test --contains -hash1=$(git rev-parse HEAD) - test_expect_success 'creating second commit and tag' ' + hash1=$(git rev-parse HEAD) && echo foo-2.0 >foo && git add foo && git commit -m second && git tag v2.0 ' -hash2=$(git rev-parse HEAD) - test_expect_success 'creating third commit without tag' ' + hash2=$(git rev-parse HEAD) && echo foo-dev >foo && git add foo && git commit -m third ' -hash3=$(git rev-parse HEAD) - # simple linear checks of --continue -cat > expected <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -v2.0 -EOF - -test_expect_success 'checking that first commit is in all tags (hash)' " +test_expect_success 'checking that first commit is in all tags (hash)' ' + hash3=$(git rev-parse HEAD) && + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + EOF git tag -l --contains $hash1 v* >actual && test_cmp expected actual -" +' # other ways of specifying the commit -test_expect_success 'checking that first commit is in all tags (tag)' " +test_expect_success 'checking that first commit is in all tags (tag)' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + EOF git tag -l --contains v1.0 v* >actual && test_cmp expected actual -" +' -test_expect_success 'checking that first commit is in all tags (relative)' " +test_expect_success 'checking that first commit is in all tags (relative)' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + EOF git tag -l --contains HEAD~2 v* >actual && test_cmp expected actual -" +' # All the --contains tests above, but with --no-contains -test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' " +test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' ' git tag -l --no-contains $hash1 v* >actual && test_must_be_empty actual -" +' -test_expect_success 'checking that first commit is in all tags (tag)' " +test_expect_success 'checking that first commit is in all tags (tag)' ' git tag -l --no-contains v1.0 v* >actual && test_must_be_empty actual -" +' -test_expect_success 'checking that first commit is in all tags (relative)' " +test_expect_success 'checking that first commit is in all tags (relative)' ' git tag -l --no-contains HEAD~2 v* >actual && test_must_be_empty actual -" - -cat > expected <<EOF -v2.0 -EOF +' -test_expect_success 'checking that second commit only has one tag' " +test_expect_success 'checking that second commit only has one tag' ' + cat >expected <<-\EOF && + v2.0 + EOF git tag -l --contains $hash2 v* >actual && test_cmp expected actual -" - -cat > expected <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -EOF +' -test_expect_success 'inverse of the last test, with --no-contains' " +test_expect_success 'inverse of the last test, with --no-contains' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + EOF git tag -l --no-contains $hash2 v* >actual && test_cmp expected actual -" +' -test_expect_success 'checking that third commit has no tags' " +test_expect_success 'checking that third commit has no tags' ' git tag -l --contains $hash3 v* >actual && test_must_be_empty actual -" - -cat > expected <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -v2.0 -EOF +' -test_expect_success 'conversely --no-contains on the third commit lists all tags' " +test_expect_success 'conversely --no-contains on the third commit lists all tags' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + EOF git tag -l --no-contains $hash3 v* >actual && test_cmp expected actual -" +' # how about a simple merge? test_expect_success 'creating simple branch' ' git branch stable v2.0 && git checkout stable && - echo foo-3.0 > foo && + echo foo-3.0 >foo && git commit foo -m fourth && git tag v3.0 ' -hash4=$(git rev-parse HEAD) - -cat > expected <<EOF -v3.0 -EOF - -test_expect_success 'checking that branch head only has one tag' " +test_expect_success 'checking that branch head only has one tag' ' + hash4=$(git rev-parse HEAD) && + cat >expected <<-\EOF && + v3.0 + EOF git tag -l --contains $hash4 v* >actual && test_cmp expected actual -" - -cat > expected <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -v2.0 -EOF +' -test_expect_success 'checking that branch head with --no-contains lists all but one tag' " +test_expect_success 'checking that branch head with --no-contains lists all but one tag' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + EOF git tag -l --no-contains $hash4 v* >actual && test_cmp expected actual -" +' test_expect_success 'merging original branch into this branch' ' git merge --strategy=ours main && git tag v4.0 ' -cat > expected <<EOF -v4.0 -EOF - -test_expect_success 'checking that original branch head has one tag now' " +test_expect_success 'checking that original branch head has one tag now' ' + cat >expected <<-\EOF && + v4.0 + EOF git tag -l --contains $hash3 v* >actual && test_cmp expected actual -" - -cat > expected <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -v2.0 -v3.0 -EOF +' -test_expect_success 'checking that original branch head with --no-contains lists all but one tag now' " +test_expect_success 'checking that original branch head with --no-contains lists all but one tag now' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + v3.0 + EOF git tag -l --no-contains $hash3 v* >actual && test_cmp expected actual -" - -cat > expected <<EOF -v0.2.1 -v1.0 -v1.0.1 -v1.1.3 -v2.0 -v3.0 -v4.0 -EOF +' -test_expect_success 'checking that initial commit is in all tags' " +test_expect_success 'checking that initial commit is in all tags' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + v3.0 + v4.0 + EOF git tag -l --contains $hash1 v* >actual && test_cmp expected actual -" +' test_expect_success 'checking that --contains can be used in non-list mode' ' + cat >expected <<-\EOF && + v0.2.1 + v1.0 + v1.0.1 + v1.1.3 + v2.0 + v3.0 + v4.0 + EOF git tag --contains $hash1 v* >actual && test_cmp expected actual ' -test_expect_success 'checking that initial commit is in all tags with --no-contains' " +test_expect_success 'checking that initial commit is in all tags with --no-contains' ' git tag -l --no-contains $hash1 v* >actual && test_must_be_empty actual -" +' # mixing modes and options: @@ -1840,16 +1794,16 @@ test_expect_success 'mixing incompatibles modes and options is forbidden' ' for option in --contains --with --no-contains --without --merged --no-merged --points-at do - test_expect_success "mixing incompatible modes with $option is forbidden" " + test_expect_success "mixing incompatible modes with $option is forbidden" ' test_must_fail git tag -d $option HEAD && test_must_fail git tag -d $option HEAD some-tag && test_must_fail git tag -v $option HEAD - " - test_expect_success "Doing 'git tag --list-like $option <commit> <pattern> is permitted" " + ' + test_expect_success "Doing 'git tag --list-like $option <commit> <pattern> is permitted" ' git tag -n $option HEAD HEAD && git tag $option HEAD HEAD && git tag $option - " + ' done # check points-at @@ -2225,7 +2179,7 @@ test_expect_success 'git tag -l with --format="%(rest)" must fail' ' test_must_fail git tag -l --format="%(rest)" "v1*" ' -test_expect_success "set up color tests" ' +test_expect_success 'set up color tests' ' echo "<RED>v1.0<RESET>" >expect.color && echo "v1.0" >expect.bare && color_args="--format=%(color:red)%(refname:short) --list v1.0" diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh index 11884d2fc3..06c1301222 100755 --- a/t/t7064-wtstatus-pv2.sh +++ b/t/t7064-wtstatus-pv2.sh @@ -4,6 +4,7 @@ test_description='git status --porcelain=v2 This test exercises porcelain V2 output for git status.' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 189d8e341b..2d984eb4c6 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -498,6 +498,19 @@ test_expect_success 'checkout unmerged stage' ' test ztheirside = "z$(cat file)" ' +test_expect_success 'checkout --ours is incompatible with switching' ' + test_must_fail git checkout --ours 2>error && + test_grep "needs the paths to check out" error && + + test_must_fail git checkout --ours HEAD 2>error && + test_grep "cannot be used with switching" error && + + test_must_fail git checkout --ours main 2>error && + test_grep "cannot be used with switching" error && + + git checkout --ours file +' + test_expect_success 'checkout path with --merge from tree-ish is a no-no' ' setup_conflicting_index && test_must_fail git checkout -m HEAD -- file diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 981488885f..098d8833b6 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -12,6 +12,7 @@ subcommands of git submodule. GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup - enable local submodules' ' diff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh index dde11ecce8..e1d9bf2ee3 100755 --- a/t/t7418-submodule-sparse-gitmodules.sh +++ b/t/t7418-submodule-sparse-gitmodules.sh @@ -15,6 +15,7 @@ also by committing .gitmodules and then just removing it from the filesystem. GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1 export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup' ' diff --git a/t/t7424-submodule-mixed-ref-formats.sh b/t/t7424-submodule-mixed-ref-formats.sh new file mode 100755 index 0000000000..b43ef2ba67 --- /dev/null +++ b/t/t7424-submodule-mixed-ref-formats.sh @@ -0,0 +1,144 @@ +#!/bin/sh + +test_description='submodules handle mixed ref storage formats' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_ref_format () { + echo "$2" >expect && + git -C "$1" rev-parse --show-ref-format >actual && + test_cmp expect actual +} + +for OTHER_FORMAT in files reftable +do + if test "$OTHER_FORMAT" = "$GIT_DEFAULT_REF_FORMAT" + then + continue + fi + +test_expect_success 'setup' ' + git config set --global protocol.file.allow always && + # Some tests migrate the ref storage format, which does not work with + # reflogs at the time of writing these tests. + git config set --global core.logAllRefUpdates false +' + +test_expect_success 'add existing repository with different ref storage format' ' + test_when_finished "rm -rf parent" && + + git init parent && + ( + cd parent && + test_commit parent && + git init --ref-format=$OTHER_FORMAT submodule && + test_commit -C submodule submodule && + git submodule add ./submodule + ) +' + +test_expect_success 'add submodules with different ref storage format' ' + test_when_finished "rm -rf submodule upstream" && + + git init submodule && + test_commit -C submodule submodule-initial && + git init upstream && + test_ref_format upstream "$GIT_DEFAULT_REF_FORMAT" && + git -C upstream submodule add --ref-format="$OTHER_FORMAT" "file://$(pwd)/submodule" && + test_ref_format upstream/submodule "$OTHER_FORMAT" +' + +test_expect_success 'recursive clone propagates ref storage format' ' + test_when_finished "rm -rf submodule upstream downstream" && + + git init submodule && + test_commit -C submodule submodule-initial && + git init upstream && + git -C upstream submodule add "file://$(pwd)/submodule" && + git -C upstream commit -am "add submodule" && + + # The upstream repository and its submodule should be using the default + # ref format. + test_ref_format upstream "$GIT_DEFAULT_REF_FORMAT" && + test_ref_format upstream/submodule "$GIT_DEFAULT_REF_FORMAT" && + + # The cloned repositories should use the other ref format that we have + # specified via `--ref-format`. The option should propagate to cloned + # submodules. + git clone --ref-format=$OTHER_FORMAT --recurse-submodules \ + upstream downstream && + test_ref_format downstream "$OTHER_FORMAT" && + test_ref_format downstream/submodule "$OTHER_FORMAT" +' + +test_expect_success 'clone submodules with different ref storage format' ' + test_when_finished "rm -rf submodule upstream downstream" && + + git init submodule && + test_commit -C submodule submodule-initial && + git init upstream && + git -C upstream submodule add "file://$(pwd)/submodule" && + git -C upstream commit -m "upstream submodule" && + + git clone --no-recurse-submodules "file://$(pwd)/upstream" downstream && + test_ref_format downstream "$GIT_DEFAULT_REF_FORMAT" && + git -C downstream submodule update --init --ref-format=$OTHER_FORMAT && + test_ref_format downstream/submodule "$OTHER_FORMAT" +' + +test_expect_success 'status with mixed submodule ref storages' ' + test_when_finished "rm -rf submodule main" && + + git init submodule && + test_commit -C submodule submodule-initial && + git init main && + git -C main submodule add "file://$(pwd)/submodule" && + git -C main commit -m "add submodule" && + git -C main/submodule refs migrate --ref-format=$OTHER_FORMAT && + + # The main repository should use the default ref format now, whereas + # the submodule should use the other format. + test_ref_format main "$GIT_DEFAULT_REF_FORMAT" && + test_ref_format main/submodule "$OTHER_FORMAT" && + + cat >expect <<-EOF && + $(git -C main/submodule rev-parse HEAD) submodule (submodule-initial) + EOF + git -C main submodule status >actual && + test_cmp expect actual +' + +test_expect_success 'recursive pull with mixed formats' ' + test_when_finished "rm -rf submodule upstream downstream" && + + # Set up the initial structure with an upstream repository that has a + # submodule, as well as a downstream clone of the upstream repository. + git init submodule && + test_commit -C submodule submodule-initial && + git init upstream && + git -C upstream submodule add "file://$(pwd)/submodule" && + git -C upstream commit -m "upstream submodule" && + + # Clone the upstream repository such that the main repo and its + # submodules have different formats. + git clone --no-recurse-submodules "file://$(pwd)/upstream" downstream && + git -C downstream submodule update --init --ref-format=$OTHER_FORMAT && + test_ref_format downstream "$GIT_DEFAULT_REF_FORMAT" && + test_ref_format downstream/submodule "$OTHER_FORMAT" && + + # Update the upstream submodule as well as the owning repository such + # that we can do a recursive pull. + test_commit -C submodule submodule-update && + git -C upstream/submodule pull && + git -C upstream commit -am "update the submodule" && + + git -C downstream pull --recurse-submodules && + git -C upstream/submodule rev-parse HEAD >expect && + git -C downstream/submodule rev-parse HEAD >actual && + test_cmp expect actual +' + +done + +test_done diff --git a/t/t7615-diff-algo-with-mergy-operations.sh b/t/t7615-diff-algo-with-mergy-operations.sh new file mode 100755 index 0000000000..9a83be518c --- /dev/null +++ b/t/t7615-diff-algo-with-mergy-operations.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +test_description='git merge and other operations that rely on merge + +Testing the influence of the diff algorithm on the merge output.' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success 'setup' ' + cp "$TEST_DIRECTORY"/t7615/base.c file.c && + git add file.c && + git commit -m c0 && + git tag c0 && + cp "$TEST_DIRECTORY"/t7615/ours.c file.c && + git add file.c && + git commit -m c1 && + git tag c1 && + git reset --hard c0 && + cp "$TEST_DIRECTORY"/t7615/theirs.c file.c && + git add file.c && + git commit -m c2 && + git tag c2 +' + +GIT_TEST_MERGE_ALGORITHM=recursive + +test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' ' + git reset --hard c1 && + test_must_fail git merge -s recursive c2 +' + +test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' ' + git reset --hard c1 && + git merge --strategy recursive -Xdiff-algorithm=histogram c2 +' + +test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' ' + git reset --hard c1 && + git config diff.algorithm histogram && + git merge --strategy recursive c2 +' + +test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' ' + git reset --hard c1 && + test_must_fail git cherry-pick -s recursive c2 +' + +test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' ' + git reset --hard c1 && + git cherry-pick --strategy recursive -Xdiff-algorithm=histogram c2 +' + +test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' ' + git reset --hard c1 && + git config diff.algorithm histogram && + git cherry-pick --strategy recursive c2 +' + +test_done diff --git a/t/t7615/base.c b/t/t7615/base.c new file mode 100644 index 0000000000..c64abc5936 --- /dev/null +++ b/t/t7615/base.c @@ -0,0 +1,17 @@ +int f(int x, int y) +{ + if (x == 0) + { + return y; + } + return x; +} + +int g(size_t u) +{ + while (u < 30) + { + u++; + } + return u; +} diff --git a/t/t7615/ours.c b/t/t7615/ours.c new file mode 100644 index 0000000000..44d8251397 --- /dev/null +++ b/t/t7615/ours.c @@ -0,0 +1,17 @@ +int g(size_t u) +{ + while (u < 30) + { + u++; + } + return u; +} + +int h(int x, int y, int z) +{ + if (z == 0) + { + return x; + } + return y; +} diff --git a/t/t7615/theirs.c b/t/t7615/theirs.c new file mode 100644 index 0000000000..85f02146fe --- /dev/null +++ b/t/t7615/theirs.c @@ -0,0 +1,17 @@ +int f(int x, int y) +{ + if (x == 0) + { + return y; + } + return x; +} + +int g(size_t u) +{ + while (u > 34) + { + u--; + } + return u; +} diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh index 71e1ef3a10..959e6e2648 100755 --- a/t/t7704-repack-cruft.sh +++ b/t/t7704-repack-cruft.sh @@ -330,7 +330,7 @@ test_expect_success '--max-cruft-size with pruning' ' # repack (and prune) with a --max-cruft-size to ensure # that we appropriately split the resulting set of packs git repack -d --cruft --max-cruft-size=1M \ - --cruft-expiration=10.seconds.ago && + --cruft-expiration=1000.seconds.ago && ls $packdir/pack-*.mtimes | sort >cruft.after && for cruft in $(cat cruft.after) diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 875dcfd98f..af2cf2f78a 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -31,6 +31,7 @@ int main(int argc, const char **argv) return 0; /* char ?? */ } + EOF test_expect_success setup ' diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 53af8e34ac..3e6dfce248 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -5,6 +5,7 @@ test_description='git p4 tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' @@ -297,8 +298,20 @@ test_expect_success 'exit when p4 fails to produce marshaled output' ' # p4 changes, files, or describe; just in p4 print. If P4CLIENT is unset, the # message will include "Librarian checkout". test_expect_success 'exit gracefully for p4 server errors' ' - test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && - mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden && + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. + case "$(echo "$db"/depot/file1*)" in + *,v) + test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && + mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden;; + *,d) + path="$(echo "$db"/depot/file1,d/*.gz)" && + test_when_finished "mv \"$path\",hidden \"$path\"" && + mv "$path" "$path",hidden;; + *) + BUG "unhandled p4d layout";; + esac && test_when_finished cleanup_git && test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err && test_grep "Error from p4 print" err diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index c598011635..cdbfacc727 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -5,6 +5,7 @@ test_description='git p4 tests for p4 branches' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh index bb236cd2b5..1bc48305b0 100755 --- a/t/t9802-git-p4-filetype.sh +++ b/t/t9802-git-p4-filetype.sh @@ -2,6 +2,7 @@ test_description='git p4 filetype tests' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' @@ -300,10 +301,22 @@ test_expect_success SYMLINKS 'empty symlink target' ' # text # @@ # + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. cd "$db/depot" && - sed "/@target1/{; s/target1/@/; n; d; }" \ - empty-symlink,v >empty-symlink,v.tmp && - mv empty-symlink,v.tmp empty-symlink,v + case "$(echo empty-symlink*)" in + empty-symlink,v) + sed "/@target1/{; s/target1/@/; n; d; }" \ + empty-symlink,v >empty-symlink,v.tmp && + mv empty-symlink,v.tmp empty-symlink,v;; + empty-symlink,d) + path="empty-symlink,d/$(ls empty-symlink,d/ | tail -n1)" && + rm "$path" && + gzip </dev/null >"$path";; + *) + BUG "unhandled p4d layout";; + esac ) && ( # Make sure symlink really is empty. Asking diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh index 2913277013..ab7fe16266 100755 --- a/t/t9803-git-p4-shell-metachars.sh +++ b/t/t9803-git-p4-shell-metachars.sh @@ -2,6 +2,7 @@ test_description='git p4 transparency to shell metachars in filenames' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh index 3236457106..c8963fd398 100755 --- a/t/t9804-git-p4-label.sh +++ b/t/t9804-git-p4-label.sh @@ -2,6 +2,7 @@ test_description='git p4 label tests' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh index 90ef647db7..72dce3d2b4 100755 --- a/t/t9805-git-p4-skip-submit-edit.sh +++ b/t/t9805-git-p4-skip-submit-edit.sh @@ -2,6 +2,7 @@ test_description='git p4 skipSubmitEdit config variables' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index c26d297433..e4ce44ebf3 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -5,6 +5,7 @@ test_description='git p4 options' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh index 58a9b3b71e..342f7f3d4a 100755 --- a/t/t9808-git-p4-chdir.sh +++ b/t/t9808-git-p4-chdir.sh @@ -2,6 +2,7 @@ test_description='git p4 relative chdir' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh index 9c9710d8c7..f33fdea889 100755 --- a/t/t9809-git-p4-client-view.sh +++ b/t/t9809-git-p4-client-view.sh @@ -2,6 +2,7 @@ test_description='git p4 client view' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh index 5fe83315ec..15e32c9f35 100755 --- a/t/t9810-git-p4-rcs.sh +++ b/t/t9810-git-p4-rcs.sh @@ -2,6 +2,7 @@ test_description='git p4 rcs keywords' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh CP1252="\223\224" diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh index 5ac5383fb7..52a4b0af81 100755 --- a/t/t9811-git-p4-label-import.sh +++ b/t/t9811-git-p4-label-import.sh @@ -5,6 +5,7 @@ test_description='git p4 label tests' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh index 254a7c2446..46aa5fd56c 100755 --- a/t/t9812-git-p4-wildcards.sh +++ b/t/t9812-git-p4-wildcards.sh @@ -2,6 +2,7 @@ test_description='git p4 wildcards' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh index fd018c87a8..0efea28da2 100755 --- a/t/t9813-git-p4-preserve-users.sh +++ b/t/t9813-git-p4-preserve-users.sh @@ -2,6 +2,7 @@ test_description='git p4 preserve users' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh index 2a9838f37f..00df6ebd3b 100755 --- a/t/t9814-git-p4-rename.sh +++ b/t/t9814-git-p4-rename.sh @@ -2,6 +2,7 @@ test_description='git p4 rename' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh index c766fd159f..92ef9d8c24 100755 --- a/t/t9815-git-p4-submit-fail.sh +++ b/t/t9815-git-p4-submit-fail.sh @@ -2,6 +2,7 @@ test_description='git p4 submit failure handling' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9816-git-p4-locked.sh b/t/t9816-git-p4-locked.sh index 5e904ac80d..e687fbc25f 100755 --- a/t/t9816-git-p4-locked.sh +++ b/t/t9816-git-p4-locked.sh @@ -2,6 +2,7 @@ test_description='git p4 locked file behavior' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9817-git-p4-exclude.sh b/t/t9817-git-p4-exclude.sh index ec3d937c6a..3deb334fed 100755 --- a/t/t9817-git-p4-exclude.sh +++ b/t/t9817-git-p4-exclude.sh @@ -2,6 +2,7 @@ test_description='git p4 tests for excluded paths during clone and sync' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9818-git-p4-block.sh b/t/t9818-git-p4-block.sh index de591d875c..091bb72bdb 100755 --- a/t/t9818-git-p4-block.sh +++ b/t/t9818-git-p4-block.sh @@ -2,6 +2,7 @@ test_description='git p4 fetching changes in multiple blocks' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9819-git-p4-case-folding.sh b/t/t9819-git-p4-case-folding.sh index b4d93f0c17..985be20357 100755 --- a/t/t9819-git-p4-case-folding.sh +++ b/t/t9819-git-p4-case-folding.sh @@ -2,6 +2,7 @@ test_description='interaction with P4 case-folding' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh if test_have_prereq CASE_INSENSITIVE_FS diff --git a/t/t9820-git-p4-editor-handling.sh b/t/t9820-git-p4-editor-handling.sh index fa1bba1dd9..48e4dfb95c 100755 --- a/t/t9820-git-p4-editor-handling.sh +++ b/t/t9820-git-p4-editor-handling.sh @@ -2,6 +2,7 @@ test_description='git p4 handling of EDITOR' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9821-git-p4-path-variations.sh b/t/t9821-git-p4-path-variations.sh index ef80f1690b..49691c53da 100755 --- a/t/t9821-git-p4-path-variations.sh +++ b/t/t9821-git-p4-path-variations.sh @@ -2,6 +2,7 @@ test_description='Clone repositories with path case variations' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d with case folding enabled' ' diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh index 572d395498..e62ed49f51 100755 --- a/t/t9822-git-p4-path-encoding.sh +++ b/t/t9822-git-p4-path-encoding.sh @@ -2,6 +2,7 @@ test_description='Clone repositories with non ASCII paths' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt" diff --git a/t/t9823-git-p4-mock-lfs.sh b/t/t9823-git-p4-mock-lfs.sh index 88b76dc4d6..98a40d8af3 100755 --- a/t/t9823-git-p4-mock-lfs.sh +++ b/t/t9823-git-p4-mock-lfs.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and store files in Mock LFS' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_file_is_not_in_mock_lfs () { diff --git a/t/t9825-git-p4-handle-utf16-without-bom.sh b/t/t9825-git-p4-handle-utf16-without-bom.sh index f049ff8229..d0b86537dd 100755 --- a/t/t9825-git-p4-handle-utf16-without-bom.sh +++ b/t/t9825-git-p4-handle-utf16-without-bom.sh @@ -2,6 +2,7 @@ test_description='git p4 handling of UTF-16 files without BOM' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh UTF16="\227\000\227\000" @@ -22,9 +23,25 @@ test_expect_success 'init depot with UTF-16 encoded file and artificially remove cd db && p4d -jc && # P4D automatically adds a BOM. Remove it here to make the file invalid. - sed -e "\$d" depot/file1,v >depot/file1,v.new && - mv depot/file1,v.new depot/file1,v && - printf "@$UTF16@" >>depot/file1,v && + # + # Note that newer Perforce versions started to store files + # compressed in directories. The case statement handles both + # old and new layout. + case "$(echo depot/file1*)" in + depot/file1,v) + sed -e "\$d" depot/file1,v >depot/file1,v.new && + mv depot/file1,v.new depot/file1,v && + printf "@$UTF16@" >>depot/file1,v;; + depot/file1,d) + path="$(echo depot/file1,d/*.gz)" && + gunzip -c "$path" >"$path.unzipped" && + sed -e "\$d" "$path.unzipped" >"$path.new" && + printf "$UTF16" >>"$path.new" && + gzip -c "$path.new" >"$path" && + rm "$path.unzipped" "$path.new";; + *) + BUG "unhandled p4d layout";; + esac && p4d -jrF checkpoint.1 ) ' diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh index fd64afe064..54083f842e 100755 --- a/t/t9826-git-p4-keep-empty-commits.sh +++ b/t/t9826-git-p4-keep-empty-commits.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and keep empty commits' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9827-git-p4-change-filetype.sh b/t/t9827-git-p4-change-filetype.sh index d3670bd7a2..3476ea2fd3 100755 --- a/t/t9827-git-p4-change-filetype.sh +++ b/t/t9827-git-p4-change-filetype.sh @@ -2,6 +2,7 @@ test_description='git p4 support for file type change' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9828-git-p4-map-user.sh b/t/t9828-git-p4-map-user.sh index ca6c2942bd..7c8f9e3930 100755 --- a/t/t9828-git-p4-map-user.sh +++ b/t/t9828-git-p4-map-user.sh @@ -2,6 +2,7 @@ test_description='Clone repositories and map users' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9829-git-p4-jobs.sh b/t/t9829-git-p4-jobs.sh index 88cfb1fcd3..3fc0948d9c 100755 --- a/t/t9829-git-p4-jobs.sh +++ b/t/t9829-git-p4-jobs.sh @@ -2,6 +2,7 @@ test_description='git p4 retrieve job info' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh index 3fb6960c18..02561a7f0e 100755 --- a/t/t9830-git-p4-symlink-dir.sh +++ b/t/t9830-git-p4-symlink-dir.sh @@ -2,6 +2,7 @@ test_description='git p4 symlinked directories' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9831-git-p4-triggers.sh b/t/t9831-git-p4-triggers.sh index ff6c0352e6..f287f41e37 100755 --- a/t/t9831-git-p4-triggers.sh +++ b/t/t9831-git-p4-triggers.sh @@ -2,6 +2,7 @@ test_description='git p4 with server triggers' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9832-unshelve.sh b/t/t9832-unshelve.sh index 6b3cb0414a..a266775408 100755 --- a/t/t9832-unshelve.sh +++ b/t/t9832-unshelve.sh @@ -6,6 +6,7 @@ last_shelved_change () { test_description='git p4 unshelve' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh index e22369ccdf..da1d30c142 100755 --- a/t/t9833-errors.sh +++ b/t/t9833-errors.sh @@ -2,6 +2,7 @@ test_description='git p4 errors' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9834-git-p4-file-dir-bug.sh b/t/t9834-git-p4-file-dir-bug.sh index dac67e89d7..565870fc74 100755 --- a/t/t9834-git-p4-file-dir-bug.sh +++ b/t/t9834-git-p4-file-dir-bug.sh @@ -6,6 +6,7 @@ This test creates files and directories with the same name in perforce and checks that git-p4 recovers from the error at the same time as the perforce repository.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh test_expect_success 'start p4d' ' diff --git a/t/t9835-git-p4-metadata-encoding-python2.sh b/t/t9835-git-p4-metadata-encoding-python2.sh index 036bf79c66..ad20ffdede 100755 --- a/t/t9835-git-p4-metadata-encoding-python2.sh +++ b/t/t9835-git-p4-metadata-encoding-python2.sh @@ -6,6 +6,7 @@ This test checks that the import process handles inconsistent text encoding in p4 metadata (author names, commit messages, etc) without failing, and produces maximally sane output in git.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh python_target_version='2' diff --git a/t/t9836-git-p4-metadata-encoding-python3.sh b/t/t9836-git-p4-metadata-encoding-python3.sh index 63350dc4b5..71ae763399 100755 --- a/t/t9836-git-p4-metadata-encoding-python3.sh +++ b/t/t9836-git-p4-metadata-encoding-python3.sh @@ -6,6 +6,7 @@ This test checks that the import process handles inconsistent text encoding in p4 metadata (author names, commit messages, etc) without failing, and produces maximally sane output in git.' +TEST_PASSES_SANITIZE_LEAK=true . ./lib-git-p4.sh python_target_version='3' diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 932d5ad759..cc6aa9f0cd 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -16,6 +16,7 @@ test_untraceable=UnfortunatelyYes GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-bash.sh complete () diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index d667dda654..95e9955bca 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -8,6 +8,7 @@ test_description='test git-specific bash prompt functions' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./lib-bash.sh . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh" diff --git a/t/unit-tests/t-example-decorate.c b/t/unit-tests/t-example-decorate.c index a4a75db735..8bf0709c41 100644 --- a/t/unit-tests/t-example-decorate.c +++ b/t/unit-tests/t-example-decorate.c @@ -15,36 +15,29 @@ static void t_add(struct test_vars *vars) { void *ret = add_decoration(&vars->n, vars->one, &vars->decoration_a); - if (!check(ret == NULL)) - test_msg("when adding a brand-new object, NULL should be returned"); + check(ret == NULL); ret = add_decoration(&vars->n, vars->two, NULL); - if (!check(ret == NULL)) - test_msg("when adding a brand-new object, NULL should be returned"); + check(ret == NULL); } static void t_readd(struct test_vars *vars) { void *ret = add_decoration(&vars->n, vars->one, NULL); - if (!check(ret == &vars->decoration_a)) - test_msg("when readding an already existing object, existing decoration should be returned"); + check(ret == &vars->decoration_a); ret = add_decoration(&vars->n, vars->two, &vars->decoration_b); - if (!check(ret == NULL)) - test_msg("when readding an already existing object, existing decoration should be returned"); + check(ret == NULL); } static void t_lookup(struct test_vars *vars) { void *ret = lookup_decoration(&vars->n, vars->one); - if (!check(ret == NULL)) - test_msg("lookup should return added declaration"); + check(ret == NULL); ret = lookup_decoration(&vars->n, vars->two); - if (!check(ret == &vars->decoration_b)) - test_msg("lookup should return added declaration"); + check(ret == &vars->decoration_b); ret = lookup_decoration(&vars->n, vars->three); - if (!check(ret == NULL)) - test_msg("lookup for unknown object should return NULL"); + check(ret == NULL); } static void t_loop(struct test_vars *vars) @@ -55,8 +48,7 @@ static void t_loop(struct test_vars *vars) if (vars->n.entries[i].base) objects_noticed++; } - if (!check_int(objects_noticed, ==, 2)) - test_msg("should have 2 objects"); + check_int(objects_noticed, ==, 2); } int cmd_main(int argc UNUSED, const char **argv UNUSED) diff --git a/t/unit-tests/t-hashmap.c b/t/unit-tests/t-hashmap.c new file mode 100644 index 0000000000..09a48c2c4e --- /dev/null +++ b/t/unit-tests/t-hashmap.c @@ -0,0 +1,361 @@ +#include "test-lib.h" +#include "hashmap.h" +#include "strbuf.h" + +struct test_entry { + int padding; /* hashmap entry no longer needs to be the first member */ + struct hashmap_entry ent; + /* key and value as two \0-terminated strings */ + char key[FLEX_ARRAY]; +}; + +static int test_entry_cmp(const void *cmp_data, + const struct hashmap_entry *eptr, + const struct hashmap_entry *entry_or_key, + const void *keydata) +{ + const unsigned int ignore_case = cmp_data ? *((int *)cmp_data) : 0; + const struct test_entry *e1, *e2; + const char *key = keydata; + + e1 = container_of(eptr, const struct test_entry, ent); + e2 = container_of(entry_or_key, const struct test_entry, ent); + + if (ignore_case) + return strcasecmp(e1->key, key ? key : e2->key); + else + return strcmp(e1->key, key ? key : e2->key); +} + +static const char *get_value(const struct test_entry *e) +{ + return e->key + strlen(e->key) + 1; +} + +static struct test_entry *alloc_test_entry(const char *key, const char *value, + unsigned int ignore_case) +{ + size_t klen = strlen(key); + size_t vlen = strlen(value); + unsigned int hash = ignore_case ? strihash(key) : strhash(key); + struct test_entry *entry = xmalloc(st_add4(sizeof(*entry), klen, vlen, 2)); + + hashmap_entry_init(&entry->ent, hash); + memcpy(entry->key, key, klen + 1); + memcpy(entry->key + klen + 1, value, vlen + 1); + return entry; +} + +static struct test_entry *get_test_entry(struct hashmap *map, const char *key, + unsigned int ignore_case) +{ + return hashmap_get_entry_from_hash( + map, ignore_case ? strihash(key) : strhash(key), key, + struct test_entry, ent); +} + +static int key_val_contains(const char *key_val[][2], char seen[], size_t n, + struct test_entry *entry) +{ + for (size_t i = 0; i < n; i++) { + if (!strcmp(entry->key, key_val[i][0]) && + !strcmp(get_value(entry), key_val[i][1])) { + if (seen[i]) + return 2; + seen[i] = 1; + return 0; + } + } + return 1; +} + +static void setup(void (*f)(struct hashmap *map, unsigned int ignore_case), + unsigned int ignore_case) +{ + struct hashmap map = HASHMAP_INIT(test_entry_cmp, &ignore_case); + + f(&map, ignore_case); + hashmap_clear_and_free(&map, struct test_entry, ent); +} + +static void t_replace(struct hashmap *map, unsigned int ignore_case) +{ + struct test_entry *entry; + + entry = alloc_test_entry("key1", "value1", ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + + entry = alloc_test_entry(ignore_case ? "Key1" : "key1", "value2", + ignore_case); + entry = hashmap_put_entry(map, entry, ent); + if (check(entry != NULL)) + check_str(get_value(entry), "value1"); + free(entry); + + entry = alloc_test_entry("fooBarFrotz", "value3", ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + + entry = alloc_test_entry(ignore_case ? "FOObarFrotz" : "fooBarFrotz", + "value4", ignore_case); + entry = hashmap_put_entry(map, entry, ent); + if (check(entry != NULL)) + check_str(get_value(entry), "value3"); + free(entry); +} + +static void t_get(struct hashmap *map, unsigned int ignore_case) +{ + struct test_entry *entry; + const char *key_val[][2] = { { "key1", "value1" }, + { "key2", "value2" }, + { "fooBarFrotz", "value3" }, + { ignore_case ? "key4" : "foobarfrotz", + "value4" } }; + const char *query[][2] = { + { ignore_case ? "Key1" : "key1", "value1" }, + { ignore_case ? "keY2" : "key2", "value2" }, + { ignore_case ? "FOObarFrotz" : "fooBarFrotz", "value3" }, + { ignore_case ? "FOObarFrotz" : "foobarfrotz", + ignore_case ? "value3" : "value4" } + }; + + for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) { + entry = alloc_test_entry(key_val[i][0], key_val[i][1], + ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + } + + for (size_t i = 0; i < ARRAY_SIZE(query); i++) { + entry = get_test_entry(map, query[i][0], ignore_case); + if (check(entry != NULL)) + check_str(get_value(entry), query[i][1]); + else + test_msg("query key: %s", query[i][0]); + } + + check_pointer_eq(get_test_entry(map, "notInMap", ignore_case), NULL); + check_int(map->tablesize, ==, 64); + check_int(hashmap_get_size(map), ==, ARRAY_SIZE(key_val)); +} + +static void t_add(struct hashmap *map, unsigned int ignore_case) +{ + struct test_entry *entry; + const char *key_val[][2] = { + { "key1", "value1" }, + { ignore_case ? "Key1" : "key1", "value2" }, + { "fooBarFrotz", "value3" }, + { ignore_case ? "FOObarFrotz" : "fooBarFrotz", "value4" } + }; + const char *query_keys[] = { "key1", ignore_case ? "FOObarFrotz" : + "fooBarFrotz" }; + char seen[ARRAY_SIZE(key_val)] = { 0 }; + + for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) { + entry = alloc_test_entry(key_val[i][0], key_val[i][1], ignore_case); + hashmap_add(map, &entry->ent); + } + + for (size_t i = 0; i < ARRAY_SIZE(query_keys); i++) { + int count = 0; + entry = hashmap_get_entry_from_hash(map, + ignore_case ? strihash(query_keys[i]) : + strhash(query_keys[i]), + query_keys[i], struct test_entry, ent); + + hashmap_for_each_entry_from(map, entry, ent) + { + int ret; + if (!check_int((ret = key_val_contains( + key_val, seen, + ARRAY_SIZE(key_val), entry)), + ==, 0)) { + switch (ret) { + case 1: + test_msg("found entry was not given in the input\n" + " key: %s\n value: %s", + entry->key, get_value(entry)); + break; + case 2: + test_msg("duplicate entry detected\n" + " key: %s\n value: %s", + entry->key, get_value(entry)); + break; + } + } else { + count++; + } + } + check_int(count, ==, 2); + } + + for (size_t i = 0; i < ARRAY_SIZE(seen); i++) { + if (!check_int(seen[i], ==, 1)) + test_msg("following key-val pair was not iterated over:\n" + " key: %s\n value: %s", + key_val[i][0], key_val[i][1]); + } + + check_int(hashmap_get_size(map), ==, ARRAY_SIZE(key_val)); + check_pointer_eq(get_test_entry(map, "notInMap", ignore_case), NULL); +} + +static void t_remove(struct hashmap *map, unsigned int ignore_case) +{ + struct test_entry *entry, *removed; + const char *key_val[][2] = { { "key1", "value1" }, + { "key2", "value2" }, + { "fooBarFrotz", "value3" } }; + const char *remove[][2] = { { ignore_case ? "Key1" : "key1", "value1" }, + { ignore_case ? "keY2" : "key2", "value2" } }; + + for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) { + entry = alloc_test_entry(key_val[i][0], key_val[i][1], ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + } + + for (size_t i = 0; i < ARRAY_SIZE(remove); i++) { + entry = alloc_test_entry(remove[i][0], "", ignore_case); + removed = hashmap_remove_entry(map, entry, ent, remove[i][0]); + if (check(removed != NULL)) + check_str(get_value(removed), remove[i][1]); + free(entry); + free(removed); + } + + entry = alloc_test_entry("notInMap", "", ignore_case); + check_pointer_eq(hashmap_remove_entry(map, entry, ent, "notInMap"), NULL); + free(entry); + + check_int(map->tablesize, ==, 64); + check_int(hashmap_get_size(map), ==, ARRAY_SIZE(key_val) - ARRAY_SIZE(remove)); +} + +static void t_iterate(struct hashmap *map, unsigned int ignore_case) +{ + struct test_entry *entry; + struct hashmap_iter iter; + const char *key_val[][2] = { { "key1", "value1" }, + { "key2", "value2" }, + { "fooBarFrotz", "value3" } }; + char seen[ARRAY_SIZE(key_val)] = { 0 }; + + for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) { + entry = alloc_test_entry(key_val[i][0], key_val[i][1], ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + } + + hashmap_for_each_entry(map, &iter, entry, ent /* member name */) + { + int ret; + if (!check_int((ret = key_val_contains(key_val, seen, + ARRAY_SIZE(key_val), + entry)), ==, 0)) { + switch (ret) { + case 1: + test_msg("found entry was not given in the input\n" + " key: %s\n value: %s", + entry->key, get_value(entry)); + break; + case 2: + test_msg("duplicate entry detected\n" + " key: %s\n value: %s", + entry->key, get_value(entry)); + break; + } + } + } + + for (size_t i = 0; i < ARRAY_SIZE(seen); i++) { + if (!check_int(seen[i], ==, 1)) + test_msg("following key-val pair was not iterated over:\n" + " key: %s\n value: %s", + key_val[i][0], key_val[i][1]); + } + + check_int(hashmap_get_size(map), ==, ARRAY_SIZE(key_val)); +} + +static void t_alloc(struct hashmap *map, unsigned int ignore_case) +{ + struct test_entry *entry, *removed; + + for (int i = 1; i <= 51; i++) { + char *key = xstrfmt("key%d", i); + char *value = xstrfmt("value%d", i); + entry = alloc_test_entry(key, value, ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + free(key); + free(value); + } + check_int(map->tablesize, ==, 64); + check_int(hashmap_get_size(map), ==, 51); + + entry = alloc_test_entry("key52", "value52", ignore_case); + check_pointer_eq(hashmap_put_entry(map, entry, ent), NULL); + check_int(map->tablesize, ==, 256); + check_int(hashmap_get_size(map), ==, 52); + + for (int i = 1; i <= 12; i++) { + char *key = xstrfmt("key%d", i); + char *value = xstrfmt("value%d", i); + + entry = alloc_test_entry(key, "", ignore_case); + removed = hashmap_remove_entry(map, entry, ent, key); + if (check(removed != NULL)) + check_str(value, get_value(removed)); + free(key); + free(value); + free(entry); + free(removed); + } + check_int(map->tablesize, ==, 256); + check_int(hashmap_get_size(map), ==, 40); + + entry = alloc_test_entry("key40", "", ignore_case); + removed = hashmap_remove_entry(map, entry, ent, "key40"); + if (check(removed != NULL)) + check_str("value40", get_value(removed)); + check_int(map->tablesize, ==, 64); + check_int(hashmap_get_size(map), ==, 39); + free(entry); + free(removed); +} + +static void t_intern(struct hashmap *map, unsigned int ignore_case) +{ + const char *values[] = { "value1", "Value1", "value2", "value2" }; + + for (size_t i = 0; i < ARRAY_SIZE(values); i++) { + const char *i1 = strintern(values[i]); + const char *i2 = strintern(values[i]); + + if (!check(!strcmp(i1, values[i]))) + test_msg("strintern(%s) returns %s\n", values[i], i1); + else if (!check(i1 != values[i])) + test_msg("strintern(%s) returns input pointer\n", + values[i]); + else if (!check_pointer_eq(i1, i2)) + test_msg("address('%s') != address('%s'), so strintern('%s') != strintern('%s')", + i1, i2, values[i], values[i]); + else + check_str(i1, values[i]); + } +} + +int cmd_main(int argc UNUSED, const char **argv UNUSED) +{ + TEST(setup(t_replace, 0), "replace works"); + TEST(setup(t_replace, 1), "replace (case insensitive) works"); + TEST(setup(t_get, 0), "get works"); + TEST(setup(t_get, 1), "get (case insensitive) works"); + TEST(setup(t_add, 0), "add works"); + TEST(setup(t_add, 1), "add (case insensitive) works"); + TEST(setup(t_remove, 0), "remove works"); + TEST(setup(t_remove, 1), "remove (case insensitive) works"); + TEST(setup(t_iterate, 0), "iterate works"); + TEST(setup(t_iterate, 1), "iterate (case insensitive) works"); + TEST(setup(t_alloc, 0), "grow / shrink works"); + TEST(setup(t_intern, 0), "string interning works"); + return test_done(); +} diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c new file mode 100644 index 0000000000..b6263ee8b5 --- /dev/null +++ b/t/unit-tests/t-reftable-merged.c @@ -0,0 +1,463 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#include "test-lib.h" +#include "reftable/blocksource.h" +#include "reftable/constants.h" +#include "reftable/merged.h" +#include "reftable/reader.h" +#include "reftable/reftable-error.h" +#include "reftable/reftable-generic.h" +#include "reftable/reftable-merged.h" +#include "reftable/reftable-writer.h" + +static ssize_t strbuf_add_void(void *b, const void *data, const size_t sz) +{ + strbuf_add(b, data, sz); + return sz; +} + +static int noop_flush(void *arg) +{ + return 0; +} + +static void write_test_table(struct strbuf *buf, + struct reftable_ref_record refs[], const size_t n) +{ + uint64_t min = 0xffffffff; + uint64_t max = 0; + size_t i; + int err; + + struct reftable_write_options opts = { + .block_size = 256, + }; + struct reftable_writer *w = NULL; + for (i = 0; i < n; i++) { + uint64_t ui = refs[i].update_index; + if (ui > max) + max = ui; + if (ui < min) + min = ui; + } + + w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); + reftable_writer_set_limits(w, min, max); + + for (i = 0; i < n; i++) { + uint64_t before = refs[i].update_index; + int n = reftable_writer_add_ref(w, &refs[i]); + check_int(n, ==, 0); + check_int(before, ==, refs[i].update_index); + } + + err = reftable_writer_close(w); + check(!err); + + reftable_writer_free(w); +} + +static void write_test_log_table(struct strbuf *buf, struct reftable_log_record logs[], + const size_t n, const uint64_t update_index) +{ + int err; + + struct reftable_write_options opts = { + .block_size = 256, + .exact_log_message = 1, + }; + struct reftable_writer *w = NULL; + w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts); + reftable_writer_set_limits(w, update_index, update_index); + + for (size_t i = 0; i < n; i++) { + int err = reftable_writer_add_log(w, &logs[i]); + check(!err); + } + + err = reftable_writer_close(w); + check(!err); + + reftable_writer_free(w); +} + +static struct reftable_merged_table * +merged_table_from_records(struct reftable_ref_record **refs, + struct reftable_block_source **source, + struct reftable_reader ***readers, const size_t *sizes, + struct strbuf *buf, const size_t n) +{ + struct reftable_merged_table *mt = NULL; + struct reftable_table *tabs; + int err; + + REFTABLE_CALLOC_ARRAY(tabs, n); + REFTABLE_CALLOC_ARRAY(*readers, n); + REFTABLE_CALLOC_ARRAY(*source, n); + + for (size_t i = 0; i < n; i++) { + write_test_table(&buf[i], refs[i], sizes[i]); + block_source_from_strbuf(&(*source)[i], &buf[i]); + + err = reftable_new_reader(&(*readers)[i], &(*source)[i], + "name"); + check(!err); + reftable_table_from_reader(&tabs[i], (*readers)[i]); + } + + err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID); + check(!err); + return mt; +} + +static void readers_destroy(struct reftable_reader **readers, const size_t n) +{ + for (size_t i = 0; i < n; i++) + reftable_reader_free(readers[i]); + reftable_free(readers); +} + +static void t_merged_single_record(void) +{ + struct reftable_ref_record r1[] = { { + .refname = (char *) "b", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 1, 2, 3, 0 }, + } }; + struct reftable_ref_record r2[] = { { + .refname = (char *) "a", + .update_index = 2, + .value_type = REFTABLE_REF_DELETION, + } }; + struct reftable_ref_record r3[] = { { + .refname = (char *) "c", + .update_index = 3, + .value_type = REFTABLE_REF_DELETION, + } }; + + struct reftable_ref_record *refs[] = { r1, r2, r3 }; + size_t sizes[] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) }; + struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; + struct reftable_block_source *bs = NULL; + struct reftable_reader **readers = NULL; + struct reftable_merged_table *mt = + merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3); + struct reftable_ref_record ref = { 0 }; + struct reftable_iterator it = { 0 }; + int err; + + merged_table_init_iter(mt, &it, BLOCK_TYPE_REF); + err = reftable_iterator_seek_ref(&it, "a"); + check(!err); + + err = reftable_iterator_next_ref(&it, &ref); + check(!err); + check(reftable_ref_record_equal(&r2[0], &ref, GIT_SHA1_RAWSZ)); + reftable_ref_record_release(&ref); + reftable_iterator_destroy(&it); + readers_destroy(readers, 3); + reftable_merged_table_free(mt); + for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) + strbuf_release(&bufs[i]); + reftable_free(bs); +} + +static void t_merged_refs(void) +{ + struct reftable_ref_record r1[] = { + { + .refname = (char *) "a", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 1 }, + }, + { + .refname = (char *) "b", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 1 }, + }, + { + .refname = (char *) "c", + .update_index = 1, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 1 }, + } + }; + struct reftable_ref_record r2[] = { { + .refname = (char *) "a", + .update_index = 2, + .value_type = REFTABLE_REF_DELETION, + } }; + struct reftable_ref_record r3[] = { + { + .refname = (char *) "c", + .update_index = 3, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 2 }, + }, + { + .refname = (char *) "d", + .update_index = 3, + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 1 }, + }, + }; + + struct reftable_ref_record *want[] = { + &r2[0], + &r1[1], + &r3[0], + &r3[1], + }; + + struct reftable_ref_record *refs[] = { r1, r2, r3 }; + size_t sizes[3] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) }; + struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; + struct reftable_block_source *bs = NULL; + struct reftable_reader **readers = NULL; + struct reftable_merged_table *mt = + merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3); + struct reftable_iterator it = { 0 }; + int err; + struct reftable_ref_record *out = NULL; + size_t len = 0; + size_t cap = 0; + size_t i; + + merged_table_init_iter(mt, &it, BLOCK_TYPE_REF); + err = reftable_iterator_seek_ref(&it, "a"); + check(!err); + check_int(reftable_merged_table_hash_id(mt), ==, GIT_SHA1_FORMAT_ID); + check_int(reftable_merged_table_min_update_index(mt), ==, 1); + check_int(reftable_merged_table_max_update_index(mt), ==, 3); + + while (len < 100) { /* cap loops/recursion. */ + struct reftable_ref_record ref = { 0 }; + int err = reftable_iterator_next_ref(&it, &ref); + if (err > 0) + break; + + REFTABLE_ALLOC_GROW(out, len + 1, cap); + out[len++] = ref; + } + reftable_iterator_destroy(&it); + + check_int(ARRAY_SIZE(want), ==, len); + for (i = 0; i < len; i++) + check(reftable_ref_record_equal(want[i], &out[i], + GIT_SHA1_RAWSZ)); + for (i = 0; i < len; i++) + reftable_ref_record_release(&out[i]); + reftable_free(out); + + for (i = 0; i < 3; i++) + strbuf_release(&bufs[i]); + readers_destroy(readers, 3); + reftable_merged_table_free(mt); + reftable_free(bs); +} + +static struct reftable_merged_table * +merged_table_from_log_records(struct reftable_log_record **logs, + struct reftable_block_source **source, + struct reftable_reader ***readers, const size_t *sizes, + struct strbuf *buf, const size_t n) +{ + struct reftable_merged_table *mt = NULL; + struct reftable_table *tabs; + int err; + + REFTABLE_CALLOC_ARRAY(tabs, n); + REFTABLE_CALLOC_ARRAY(*readers, n); + REFTABLE_CALLOC_ARRAY(*source, n); + + for (size_t i = 0; i < n; i++) { + write_test_log_table(&buf[i], logs[i], sizes[i], i + 1); + block_source_from_strbuf(&(*source)[i], &buf[i]); + + err = reftable_new_reader(&(*readers)[i], &(*source)[i], + "name"); + check(!err); + reftable_table_from_reader(&tabs[i], (*readers)[i]); + } + + err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID); + check(!err); + return mt; +} + +static void t_merged_logs(void) +{ + struct reftable_log_record r1[] = { + { + .refname = (char *) "a", + .update_index = 2, + .value_type = REFTABLE_LOG_UPDATE, + .value.update = { + .old_hash = { 2 }, + /* deletion */ + .name = (char *) "jane doe", + .email = (char *) "jane@invalid", + .message = (char *) "message2", + } + }, + { + .refname = (char *) "a", + .update_index = 1, + .value_type = REFTABLE_LOG_UPDATE, + .value.update = { + .old_hash = { 1 }, + .new_hash = { 2 }, + .name = (char *) "jane doe", + .email = (char *) "jane@invalid", + .message = (char *) "message1", + } + }, + }; + struct reftable_log_record r2[] = { + { + .refname = (char *) "a", + .update_index = 3, + .value_type = REFTABLE_LOG_UPDATE, + .value.update = { + .new_hash = { 3 }, + .name = (char *) "jane doe", + .email = (char *) "jane@invalid", + .message = (char *) "message3", + } + }, + }; + struct reftable_log_record r3[] = { + { + .refname = (char *) "a", + .update_index = 2, + .value_type = REFTABLE_LOG_DELETION, + }, + }; + struct reftable_log_record *want[] = { + &r2[0], + &r3[0], + &r1[1], + }; + + struct reftable_log_record *logs[] = { r1, r2, r3 }; + size_t sizes[3] = { ARRAY_SIZE(r1), ARRAY_SIZE(r2), ARRAY_SIZE(r3) }; + struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; + struct reftable_block_source *bs = NULL; + struct reftable_reader **readers = NULL; + struct reftable_merged_table *mt = merged_table_from_log_records( + logs, &bs, &readers, sizes, bufs, 3); + struct reftable_iterator it = { 0 }; + int err; + struct reftable_log_record *out = NULL; + size_t len = 0; + size_t cap = 0; + size_t i; + + merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG); + err = reftable_iterator_seek_log(&it, "a"); + check(!err); + check_int(reftable_merged_table_hash_id(mt), ==, GIT_SHA1_FORMAT_ID); + check_int(reftable_merged_table_min_update_index(mt), ==, 1); + check_int(reftable_merged_table_max_update_index(mt), ==, 3); + + while (len < 100) { /* cap loops/recursion. */ + struct reftable_log_record log = { 0 }; + int err = reftable_iterator_next_log(&it, &log); + if (err > 0) + break; + + REFTABLE_ALLOC_GROW(out, len + 1, cap); + out[len++] = log; + } + reftable_iterator_destroy(&it); + + check_int(ARRAY_SIZE(want), ==, len); + for (i = 0; i < len; i++) + check(reftable_log_record_equal(want[i], &out[i], + GIT_SHA1_RAWSZ)); + + merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG); + err = reftable_iterator_seek_log_at(&it, "a", 2); + check(!err); + reftable_log_record_release(&out[0]); + err = reftable_iterator_next_log(&it, &out[0]); + check(!err); + check(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ)); + reftable_iterator_destroy(&it); + + for (i = 0; i < len; i++) + reftable_log_record_release(&out[i]); + reftable_free(out); + + for (i = 0; i < 3; i++) + strbuf_release(&bufs[i]); + readers_destroy(readers, 3); + reftable_merged_table_free(mt); + reftable_free(bs); +} + +static void t_default_write_opts(void) +{ + struct reftable_write_options opts = { 0 }; + struct strbuf buf = STRBUF_INIT; + struct reftable_writer *w = + reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts); + + struct reftable_ref_record rec = { + .refname = (char *) "master", + .update_index = 1, + }; + int err; + struct reftable_block_source source = { 0 }; + struct reftable_table *tab = reftable_calloc(1, sizeof(*tab)); + uint32_t hash_id; + struct reftable_reader *rd = NULL; + struct reftable_merged_table *merged = NULL; + + reftable_writer_set_limits(w, 1, 1); + + err = reftable_writer_add_ref(w, &rec); + check(!err); + + err = reftable_writer_close(w); + check(!err); + reftable_writer_free(w); + + block_source_from_strbuf(&source, &buf); + + err = reftable_new_reader(&rd, &source, "filename"); + check(!err); + + hash_id = reftable_reader_hash_id(rd); + check_int(hash_id, ==, GIT_SHA1_FORMAT_ID); + + reftable_table_from_reader(&tab[0], rd); + err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA256_FORMAT_ID); + check_int(err, ==, REFTABLE_FORMAT_ERROR); + err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID); + check(!err); + + reftable_reader_free(rd); + reftable_merged_table_free(merged); + strbuf_release(&buf); +} + + +int cmd_main(int argc, const char *argv[]) +{ + TEST(t_default_write_opts(), "merged table with default write opts"); + TEST(t_merged_logs(), "merged table with multiple log updates for same ref"); + TEST(t_merged_refs(), "merged table with multiple updates to same ref"); + TEST(t_merged_single_record(), "ref ocurring in only one record can be fetched"); + + return test_done(); +} diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c new file mode 100644 index 0000000000..039bd0f1f9 --- /dev/null +++ b/t/unit-tests/t-reftable-pq.c @@ -0,0 +1,152 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#include "test-lib.h" +#include "reftable/constants.h" +#include "reftable/pq.h" + +static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq) +{ + for (size_t i = 1; i < pq->len; i++) { + size_t parent = (i - 1) / 2; + check(pq_less(&pq->heap[parent], &pq->heap[i])); + } +} + +static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b) +{ + return !reftable_record_cmp(a->rec, b->rec) && (a->index == b->index); +} + +static void t_pq_record(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[54]; + size_t N = ARRAY_SIZE(recs) - 1, i; + char *last = NULL; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = xstrfmt("%02"PRIuMAX, (uintmax_t)i); + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + while (!merged_iter_pqueue_is_empty(pq)) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + merged_iter_pqueue_check(&pq); + + check(pq_entry_equal(&top, &e)); + check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); + if (last) + check_int(strcmp(last, e.rec->u.ref.refname), <, 0); + last = e.rec->u.ref.refname; + } + + for (i = 0; i < N; i++) + reftable_record_release(&recs[i]); + merged_iter_pqueue_release(&pq); +} + +static void t_pq_index(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[13]; + char *last = NULL; + size_t N = ARRAY_SIZE(recs), i; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = (char *) "refs/heads/master"; + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + .index = i, + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + for (i = N - 1; i > 0; i--) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + merged_iter_pqueue_check(&pq); + + check(pq_entry_equal(&top, &e)); + check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); + check_int(e.index, ==, i); + if (last) + check_str(last, e.rec->u.ref.refname); + last = e.rec->u.ref.refname; + } + + merged_iter_pqueue_release(&pq); +} + +static void t_merged_iter_pqueue_top(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[13]; + size_t N = ARRAY_SIZE(recs), i; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = (char *) "refs/heads/master"; + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + .index = i, + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + for (i = N - 1; i > 0; i--) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + + merged_iter_pqueue_check(&pq); + check(pq_entry_equal(&top, &e)); + check(reftable_record_equal(top.rec, &recs[i], GIT_SHA1_RAWSZ)); + for (size_t j = 0; i < pq.len; j++) { + check(pq_less(&top, &pq.heap[j])); + check_int(top.index, >, j); + } + } + + merged_iter_pqueue_release(&pq); +} + +int cmd_main(int argc, const char *argv[]) +{ + TEST(t_pq_record(), "pq works with record-based comparison"); + TEST(t_pq_index(), "pq works with index-based comparison"); + TEST(t_merged_iter_pqueue_top(), "merged_iter_pqueue_top works"); + + return test_done(); +} diff --git a/t/unit-tests/t-reftable-tree.c b/t/unit-tests/t-reftable-tree.c new file mode 100644 index 0000000000..e7d774d774 --- /dev/null +++ b/t/unit-tests/t-reftable-tree.c @@ -0,0 +1,84 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#include "test-lib.h" +#include "reftable/tree.h" + +static int t_compare(const void *a, const void *b) +{ + return (char *)a - (char *)b; +} + +struct curry { + void **arr; + size_t len; +}; + +static void store(void *arg, void *key) +{ + struct curry *c = arg; + c->arr[c->len++] = key; +} + +static void t_tree_search(void) +{ + struct tree_node *root = NULL; + void *values[11] = { 0 }; + struct tree_node *nodes[11] = { 0 }; + size_t i = 1; + + /* + * Pseudo-randomly insert the pointers for elements between + * values[1] and values[10] (inclusive) in the tree. + */ + do { + nodes[i] = tree_search(&values[i], &root, &t_compare, 1); + i = (i * 7) % 11; + } while (i != 1); + + for (i = 1; i < ARRAY_SIZE(nodes); i++) { + check_pointer_eq(&values[i], nodes[i]->key); + check_pointer_eq(nodes[i], tree_search(&values[i], &root, &t_compare, 0)); + } + + check(!tree_search(values, &root, t_compare, 0)); + tree_free(root); +} + +static void t_infix_walk(void) +{ + struct tree_node *root = NULL; + void *values[11] = { 0 }; + void *out[11] = { 0 }; + struct curry c = { + .arr = (void **) &out, + }; + size_t i = 1; + size_t count = 0; + + do { + tree_search(&values[i], &root, t_compare, 1); + i = (i * 7) % 11; + count++; + } while (i != 1); + + infix_walk(root, &store, &c); + for (i = 1; i < ARRAY_SIZE(values); i++) + check_pointer_eq(&values[i], out[i - 1]); + check(!out[i - 1]); + check_int(c.len, ==, count); + tree_free(root); +} + +int cmd_main(int argc, const char *argv[]) +{ + TEST(t_tree_search(), "tree_search works"); + TEST(t_infix_walk(), "infix_walk works"); + + return test_done(); +} diff --git a/t/unit-tests/t-strvec.c b/t/unit-tests/t-strvec.c index d4615ab06d..fa1a041469 100644 --- a/t/unit-tests/t-strvec.c +++ b/t/unit-tests/t-strvec.c @@ -3,38 +3,21 @@ #include "strvec.h" #define check_strvec(vec, ...) \ - check_strvec_loc(TEST_LOCATION(), vec, __VA_ARGS__) -LAST_ARG_MUST_BE_NULL -static void check_strvec_loc(const char *loc, struct strvec *vec, ...) -{ - va_list ap; - size_t nr = 0; - - va_start(ap, vec); - while (1) { - const char *str = va_arg(ap, const char *); - if (!str) - break; - - if (!check_uint(vec->nr, >, nr) || - !check_uint(vec->alloc, >, nr) || - !check_str(vec->v[nr], str)) { - struct strbuf msg = STRBUF_INIT; - strbuf_addf(&msg, "strvec index %"PRIuMAX, (uintmax_t) nr); - test_assert(loc, msg.buf, 0); - strbuf_release(&msg); - va_end(ap); - return; - } - - nr++; - } - va_end(ap); - - check_uint(vec->nr, ==, nr); - check_uint(vec->alloc, >=, nr); - check_pointer_eq(vec->v[nr], NULL); -} + do { \ + const char *expect[] = { __VA_ARGS__ }; \ + if (check_uint(ARRAY_SIZE(expect), >, 0) && \ + check_pointer_eq(expect[ARRAY_SIZE(expect) - 1], NULL) && \ + check_uint((vec)->nr, ==, ARRAY_SIZE(expect) - 1) && \ + check_uint((vec)->nr, <=, (vec)->alloc)) { \ + for (size_t i = 0; i < ARRAY_SIZE(expect); i++) { \ + if (!check_str((vec)->v[i], expect[i])) { \ + test_msg(" i: %"PRIuMAX, \ + (uintmax_t)i); \ + break; \ + } \ + } \ + } \ + } while (0) static void t_static_init(void) { diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h index 2de6d715d5..c59f646fd9 100644 --- a/t/unit-tests/test-lib.h +++ b/t/unit-tests/test-lib.h @@ -76,8 +76,9 @@ int test_assert(const char *location, const char *check, int ok); int check_bool_loc(const char *loc, const char *check, int ok); /* - * Compare two integers. Prints a message with the two values if the - * comparison fails. NB this is not thread safe. + * Compare the equality of two pointers of same type. Prints a message + * with the two values if the equality fails. NB this is not thread + * safe. */ #define check_pointer_eq(a, b) \ (test__tmp[0].p = (a), test__tmp[1].p = (b), \ |
