t/: migrate helper/test-oidtree.c to unit-tests/t-oidtree.c

helper/test-oidtree.c along with t0069-oidtree.sh test the oidtree.h library, which is a wrapper around crit-bit tree. Migrate them to the unit testing framework for better debugging and runtime performance. Along with the migration, add an extra check for oidtree_each() test, which showcases how multiple expected matches can be given to check_each() helper. To achieve this, introduce a new library called 'lib-oid.h' exclusively for the unit tests to use. It currently mainly includes utility to generate object_id from an arbitrary hex string (i.e. '12a' -> '12a0000000000000000000000000000000000000'). This also handles the hash algo selection based on GIT_TEST_DEFAULT_HASH. This library will also be helpful when we port other unit tests such as oid-array, oidset etc. Helped-by: Junio C Hamano <gitster@pobox.com> Mentored-by: Christian Couder <chriscool@tuxfamily.org> Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com> Signed-off-by: Ghanshyam Thakkar <shyamthakkar001@gmail.com> [jc: small fixlets squashed in] Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ghanshyam Thakkar committed Jun 8, 2024 at 22:27 UTC ed548408723d6e969160279398cc47f88f5700bc
8 files changed +197 -108
Makefile
+6 -2
@@ -811,7 +811,6 @@ TEST_BUILTINS_OBJS += test-mergesort.o
811 TEST_BUILTINS_OBJS += test-mktemp.o
812 TEST_BUILTINS_OBJS += test-oid-array.o
813 TEST_BUILTINS_OBJS += test-oidmap.o
814 -TEST_BUILTINS_OBJS += test-oidtree.o
814 TEST_BUILTINS_OBJS += test-online-cpus.o
815 TEST_BUILTINS_OBJS += test-pack-mtimes.o
816 TEST_BUILTINS_OBJS += test-parse-options.o
@@ -1335,6 +1334,7 @@ THIRD_PARTY_SOURCES += sha1dc/%
1334
1335 UNIT_TEST_PROGRAMS += t-ctype
1336 UNIT_TEST_PROGRAMS += t-mem-pool
1337 +UNIT_TEST_PROGRAMS += t-oidtree
1338 UNIT_TEST_PROGRAMS += t-prio-queue
1339 UNIT_TEST_PROGRAMS += t-strbuf
1340 UNIT_TEST_PROGRAMS += t-strcmp-offset
@@ -1343,6 +1343,7 @@ UNIT_TEST_PROGRAMS += t-trailer
1343 UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
1344 UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
1345 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
1346 +UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
1347
1348 # xdiff and reftable libs may in turn depend on what is in libgit.a
1349 GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
@@ -3883,7 +3884,10 @@ $(FUZZ_PROGRAMS): %: %.o oss-fuzz/dummy-cmd-main.o $(GITLIBS) GIT-LDFLAGS
3884 -Wl,--allow-multiple-definition \
3885 $(filter %.o,$^) $(filter %.a,$^) $(LIBS) $(LIB_FUZZING_ENGINE)
3886
3886 -$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS
3887 +$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o \
3888 + $(UNIT_TEST_DIR)/test-lib.o \
3889 + $(UNIT_TEST_DIR)/lib-oid.o \
3890 + $(GITLIBS) GIT-LDFLAGS
3891 $(call mkdir_p_parent_template)
3892 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
3893 $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
t/helper/test-oidtree.c deleted
-54
@@ -1,54 +0,0 @@
1 -#include "test-tool.h"
2 -#include "hex.h"
3 -#include "oidtree.h"
4 -#include "setup.h"
5 -#include "strbuf.h"
6 -
7 -static enum cb_next print_oid(const struct object_id *oid, void *data UNUSED)
8 -{
9 - puts(oid_to_hex(oid));
10 - return CB_CONTINUE;
11 -}
12 -
13 -int cmd__oidtree(int argc UNUSED, const char **argv UNUSED)
14 -{
15 - struct oidtree ot;
16 - struct strbuf line = STRBUF_INIT;
17 - int nongit_ok;
18 - int algo = GIT_HASH_UNKNOWN;
19 -
20 - oidtree_init(&ot);
21 - setup_git_directory_gently(&nongit_ok);
22 -
23 - while (strbuf_getline(&line, stdin) != EOF) {
24 - const char *arg;
25 - struct object_id oid;
26 -
27 - if (skip_prefix(line.buf, "insert ", &arg)) {
28 - if (get_oid_hex_any(arg, &oid) == GIT_HASH_UNKNOWN)
29 - die("insert not a hexadecimal oid: %s", arg);
30 - algo = oid.algo;
31 - oidtree_insert(&ot, &oid);
32 - } else if (skip_prefix(line.buf, "contains ", &arg)) {
33 - if (get_oid_hex(arg, &oid))
34 - die("contains not a hexadecimal oid: %s", arg);
35 - printf("%d\n", oidtree_contains(&ot, &oid));
36 - } else if (skip_prefix(line.buf, "each ", &arg)) {
37 - char buf[GIT_MAX_HEXSZ + 1] = { '0' };
38 - memset(&oid, 0, sizeof(oid));
39 - memcpy(buf, arg, strlen(arg));
40 - buf[hash_algos[algo].hexsz] = '\0';
41 - get_oid_hex_any(buf, &oid);
42 - oid.algo = algo;
43 - oidtree_each(&ot, &oid, strlen(arg), print_oid, NULL);
44 - } else if (!strcmp(line.buf, "clear")) {
45 - oidtree_clear(&ot);
46 - } else {
47 - die("unknown command: %s", line.buf);
48 - }
49 - }
50 -
51 - strbuf_release(&line);
52 -
53 - return 0;
54 -}
t/helper/test-tool.c
-1
@@ -46,7 +46,6 @@ static struct test_cmd cmds[] = {
46 { "mktemp", cmd__mktemp },
47 { "oid-array", cmd__oid_array },
48 { "oidmap", cmd__oidmap },
49 - { "oidtree", cmd__oidtree },
49 { "online-cpus", cmd__online_cpus },
50 { "pack-mtimes", cmd__pack_mtimes },
51 { "parse-options", cmd__parse_options },
t/helper/test-tool.h
-1
@@ -39,7 +39,6 @@ int cmd__match_trees(int argc, const char **argv);
39 int cmd__mergesort(int argc, const char **argv);
40 int cmd__mktemp(int argc, const char **argv);
41 int cmd__oidmap(int argc, const char **argv);
42 -int cmd__oidtree(int argc, const char **argv);
42 int cmd__online_cpus(int argc, const char **argv);
43 int cmd__pack_mtimes(int argc, const char **argv);
44 int cmd__parse_options(int argc, const char **argv);
t/t0069-oidtree.sh deleted
-50
@@ -1,50 +0,0 @@
1 -#!/bin/sh
2 -
3 -test_description='basic tests for the oidtree implementation'
4 -TEST_PASSES_SANITIZE_LEAK=true
5 -. ./test-lib.sh
6 -
7 -maxhexsz=$(test_oid hexsz)
8 -echoid () {
9 - prefix="${1:+$1 }"
10 - shift
11 - while test $# -gt 0
12 - do
13 - shortoid="$1"
14 - shift
15 - difference=$(($maxhexsz - ${#shortoid}))
16 - printf "%s%s%0${difference}d\\n" "$prefix" "$shortoid" "0"
17 - done
18 -}
19 -
20 -test_expect_success 'oidtree insert and contains' '
21 - cat >expect <<-\EOF &&
22 - 0
23 - 0
24 - 0
25 - 1
26 - 1
27 - 0
28 - EOF
29 - {
30 - echoid insert 444 1 2 3 4 5 a b c d e &&
31 - echoid contains 44 441 440 444 4440 4444 &&
32 - echo clear
33 - } | test-tool oidtree >actual &&
34 - test_cmp expect actual
35 -'
36 -
37 -test_expect_success 'oidtree each' '
38 - echoid "" 123 321 321 >expect &&
39 - {
40 - echoid insert f 9 8 123 321 a b c d e &&
41 - echo each 12300 &&
42 - echo each 3211 &&
43 - echo each 3210 &&
44 - echo each 32100 &&
45 - echo clear
46 - } | test-tool oidtree >actual &&
47 - test_cmp expect actual
48 -'
49 -
50 -test_done
t/unit-tests/lib-oid.c new
+52
@@ -0,0 +1,52 @@
1 +#include "test-lib.h"
2 +#include "lib-oid.h"
3 +#include "strbuf.h"
4 +#include "hex.h"
5 +
6 +static int init_hash_algo(void)
7 +{
8 + static int algo = -1;
9 +
10 + if (algo < 0) {
11 + const char *algo_name = getenv("GIT_TEST_DEFAULT_HASH");
12 + algo = algo_name ? hash_algo_by_name(algo_name) : GIT_HASH_SHA1;
13 +
14 + if (!check(algo != GIT_HASH_UNKNOWN))
15 + test_msg("BUG: invalid GIT_TEST_DEFAULT_HASH value ('%s')",
16 + algo_name);
17 + }
18 + return algo;
19 +}
20 +
21 +static int get_oid_arbitrary_hex_algop(const char *hex, struct object_id *oid,
22 + const struct git_hash_algo *algop)
23 +{
24 + int ret;
25 + size_t sz = strlen(hex);
26 + struct strbuf buf = STRBUF_INIT;
27 +
28 + if (!check(sz <= algop->hexsz)) {
29 + test_msg("BUG: hex string (%s) bigger than maximum allowed (%lu)",
30 + hex, (unsigned long)algop->hexsz);
31 + return -1;
32 + }
33 +
34 + strbuf_add(&buf, hex, sz);
35 + strbuf_addchars(&buf, '0', algop->hexsz - sz);
36 +
37 + ret = get_oid_hex_algop(buf.buf, oid, algop);
38 + if (!check_int(ret, ==, 0))
39 + test_msg("BUG: invalid hex input (%s) provided", hex);
40 +
41 + strbuf_release(&buf);
42 + return ret;
43 +}
44 +
45 +int get_oid_arbitrary_hex(const char *hex, struct object_id *oid)
46 +{
47 + int hash_algo = init_hash_algo();
48 +
49 + if (!check_int(hash_algo, !=, GIT_HASH_UNKNOWN))
50 + return -1;
51 + return get_oid_arbitrary_hex_algop(hex, oid, &hash_algos[hash_algo]);
52 +}
t/unit-tests/lib-oid.h new
+17
@@ -0,0 +1,17 @@
1 +#ifndef LIB_OID_H
2 +#define LIB_OID_H
3 +
4 +#include "hash-ll.h"
5 +
6 +/*
7 + * Convert arbitrary hex string to object_id.
8 + * For example, passing "abc12" will generate
9 + * "abc1200000000000000000000000000000000000" hex of length 40 for SHA-1 and
10 + * create object_id with that.
11 + * WARNING: passing a string of length more than the hexsz of respective hash
12 + * algo is not allowed. The hash algo is decided based on GIT_TEST_DEFAULT_HASH
13 + * environment variable.
14 + */
15 +int get_oid_arbitrary_hex(const char *s, struct object_id *oid);
16 +
17 +#endif /* LIB_OID_H */
t/unit-tests/t-oidtree.c new
+122
@@ -0,0 +1,122 @@
1 +#include "test-lib.h"
2 +#include "lib-oid.h"
3 +#include "oidtree.h"
4 +#include "hash.h"
5 +#include "hex.h"
6 +#include "strvec.h"
7 +
8 +#define FILL_TREE(tree, ...) \
9 + do { \
10 + const char *hexes[] = { __VA_ARGS__ }; \
11 + if (fill_tree_loc(tree, hexes, ARRAY_SIZE(hexes))) \
12 + return; \
13 + } while (0)
14 +
15 +static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
16 +{
17 + for (size_t i = 0; i < n; i++) {
18 + struct object_id oid;
19 + if (!check_int(get_oid_arbitrary_hex(hexes[i], &oid), ==, 0))
20 + return -1;
21 + oidtree_insert(ot, &oid);
22 + }
23 + return 0;
24 +}
25 +
26 +static void check_contains(struct oidtree *ot, const char *hex, int expected)
27 +{
28 + struct object_id oid;
29 +
30 + if (!check_int(get_oid_arbitrary_hex(hex, &oid), ==, 0))
31 + return;
32 + if (!check_int(oidtree_contains(ot, &oid), ==, expected))
33 + test_msg("oid: %s", oid_to_hex(&oid));
34 +}
35 +
36 +struct expected_hex_iter {
37 + size_t i;
38 + struct strvec expected_hexes;
39 + const char *query;
40 +};
41 +
42 +static enum cb_next check_each_cb(const struct object_id *oid, void *data)
43 +{
44 + struct expected_hex_iter *hex_iter = data;
45 + struct object_id expected;
46 +
47 + if (!check_int(hex_iter->i, <, hex_iter->expected_hexes.nr)) {
48 + test_msg("error: extraneous callback for query: ('%s'), object_id: ('%s')",
49 + hex_iter->query, oid_to_hex(oid));
50 + return CB_BREAK;
51 + }
52 +
53 + if (!check_int(get_oid_arbitrary_hex(hex_iter->expected_hexes.v[hex_iter->i],
54 + &expected), ==, 0))
55 + ; /* the data is bogus and cannot be used */
56 + else if (!check(oideq(oid, &expected)))
57 + test_msg("expected: %s\n got: %s\n query: %s",
58 + oid_to_hex(&expected), oid_to_hex(oid), hex_iter->query);
59 +
60 + hex_iter->i += 1;
61 + return CB_CONTINUE;
62 +}
63 +
64 +LAST_ARG_MUST_BE_NULL
65 +static void check_each(struct oidtree *ot, const char *query, ...)
66 +{
67 + struct object_id oid;
68 + struct expected_hex_iter hex_iter = { .expected_hexes = STRVEC_INIT,
69 + .query = query };
70 + const char *arg;
71 + va_list hex_args;
72 +
73 + va_start(hex_args, query);
74 + while ((arg = va_arg(hex_args, const char *)))
75 + strvec_push(&hex_iter.expected_hexes, arg);
76 + va_end(hex_args);
77 +
78 + if (!check_int(get_oid_arbitrary_hex(query, &oid), ==, 0))
79 + return;
80 + oidtree_each(ot, &oid, strlen(query), check_each_cb, &hex_iter);
81 +
82 + if (!check_int(hex_iter.i, ==, hex_iter.expected_hexes.nr))
83 + test_msg("error: could not find some 'object_id's for query ('%s')", query);
84 + strvec_clear(&hex_iter.expected_hexes);
85 +}
86 +
87 +static void setup(void (*f)(struct oidtree *ot))
88 +{
89 + struct oidtree ot;
90 +
91 + oidtree_init(&ot);
92 + f(&ot);
93 + oidtree_clear(&ot);
94 +}
95 +
96 +static void t_contains(struct oidtree *ot)
97 +{
98 + FILL_TREE(ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e");
99 + check_contains(ot, "44", 0);
100 + check_contains(ot, "441", 0);
101 + check_contains(ot, "440", 0);
102 + check_contains(ot, "444", 1);
103 + check_contains(ot, "4440", 1);
104 + check_contains(ot, "4444", 0);
105 +}
106 +
107 +static void t_each(struct oidtree *ot)
108 +{
109 + FILL_TREE(ot, "f", "9", "8", "123", "321", "320", "a", "b", "c", "d", "e");
110 + check_each(ot, "12300", "123", NULL);
111 + check_each(ot, "3211", NULL); /* should not reach callback */
112 + check_each(ot, "3210", "321", NULL);
113 + check_each(ot, "32100", "321", NULL);
114 + check_each(ot, "32", "320", "321", NULL);
115 +}
116 +
117 +int cmd_main(int argc UNUSED, const char **argv UNUSED)
118 +{
119 + TEST(setup(t_contains), "oidtree insert and contains works");
120 + TEST(setup(t_each), "oidtree each works");
121 + return test_done();
122 +}