* [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h}
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-04-29 23:04 ` Junio C Hamano
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:52 ` [PATCH v2 02/10] t/unit-tests: convert reftable basics test to use clar test framework Seyi Kuforiji
` (8 subsequent siblings)
9 siblings, 2 replies; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Helper functions defined in `t/unit-tests/lib-reftable.{c,h}` are
required for the reftable-related test files to run efficeintly. In the
current implementation these functions are designed to conform with our
homegrown unit-testing structure. So in other to convert the reftable
test files, there is need for a clar specific implementation of these
helper functions.
type cast `for (size_t i = 0; i < (size_t)stats->ref_stats.blocks;
i++)`, implement equivalent helper functions in unit-test.{c,h} to use
clar. These functions conform with the clar testing framework and become
available for all reftable-related test files implemented using the clar
testing framework, which requires them. This will be used by subsequent
commits.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
t/unit-tests/unit-test.c | 93 ++++++++++++++++++++++++++++++++++++++++
t/unit-tests/unit-test.h | 16 +++++++
2 files changed, 109 insertions(+)
diff --git a/t/unit-tests/unit-test.c b/t/unit-tests/unit-test.c
index 5af645048a..6c2a4e6aa8 100644
--- a/t/unit-tests/unit-test.c
+++ b/t/unit-tests/unit-test.c
@@ -1,10 +1,103 @@
#include "unit-test.h"
#include "hex.h"
#include "parse-options.h"
+#include "reftable/constants.h"
+#include "reftable/writer.h"
#include "strbuf.h"
#include "string-list.h"
#include "strvec.h"
+void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id)
+{
+ memset(p, (uint8_t)i, hash_size(id));
+}
+
+static ssize_t strbuf_writer_write(void *b, const void *data, size_t sz)
+{
+ strbuf_add(b, data, sz);
+ return sz;
+}
+
+static int strbuf_writer_flush(void *arg UNUSED)
+{
+ return 0;
+}
+
+struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
+ struct reftable_write_options *opts)
+{
+ struct reftable_writer *writer;
+ int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush,
+ buf, opts);
+ cl_assert(ret == 0);
+ return writer;
+}
+
+void cl_reftable_write_to_buf(struct reftable_buf *buf,
+ struct reftable_ref_record *refs,
+ size_t nrefs,
+ struct reftable_log_record *logs,
+ size_t nlogs,
+ struct reftable_write_options *_opts)
+{
+ struct reftable_write_options opts = { 0 };
+ const struct reftable_stats *stats;
+ struct reftable_writer *writer;
+ uint64_t min = 0xffffffff;
+ uint64_t max = 0;
+ int ret;
+
+ if (_opts)
+ opts = *_opts;
+
+ for (size_t i = 0; i < nrefs; i++) {
+ uint64_t ui = refs[i].update_index;
+ if (ui > max)
+ max = ui;
+ if (ui < min)
+ min = ui;
+ }
+ for (size_t i = 0; i < nlogs; i++) {
+ uint64_t ui = logs[i].update_index;
+ if (ui > max)
+ max = ui;
+ if (ui < min)
+ min = ui;
+ }
+
+ writer = cl_reftable_strbuf_writer(buf, &opts);
+ reftable_writer_set_limits(writer, min, max);
+
+ if (nrefs) {
+ ret = reftable_writer_add_refs(writer, refs, nrefs);
+ cl_assert_equal_i(ret, 0);
+ }
+
+ if (nlogs) {
+ ret = reftable_writer_add_logs(writer, logs, nlogs);
+ cl_assert_equal_i(ret, 0);
+ }
+
+ ret = reftable_writer_close(writer);
+ cl_assert_equal_i(ret, 0);
+
+ stats = reftable_writer_stats(writer);
+ for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++) {
+ size_t off = i * (opts.block_size ? opts.block_size
+ : DEFAULT_BLOCK_SIZE);
+ if (!off)
+ off = header_size(opts.hash_id == REFTABLE_HASH_SHA256 ? 2 : 1);
+ cl_assert(buf->buf[off] == 'r');
+ }
+
+ if (nrefs)
+ cl_assert(stats->ref_stats.blocks > 0);
+ if (nlogs)
+ cl_assert(stats->log_stats.blocks > 0);
+
+ reftable_writer_free(writer);
+}
+
static const char * const unit_test_usage[] = {
N_("unit-test [<options>]"),
NULL,
diff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h
index 85e5d6a948..fe0aebd876 100644
--- a/t/unit-tests/unit-test.h
+++ b/t/unit-tests/unit-test.h
@@ -1,8 +1,24 @@
#include "git-compat-util.h"
#include "clar/clar.h"
#include "clar-decls.h"
+#include "git-compat-util.h"
+#include "reftable/reftable-writer.h"
#include "strbuf.h"
+struct reftable_buf;
+
+void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
+
+struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
+ struct reftable_write_options *opts);
+
+void cl_reftable_write_to_buf(struct reftable_buf *buf,
+ struct reftable_ref_record *refs,
+ size_t nrecords,
+ struct reftable_log_record *logs,
+ size_t nlogs,
+ struct reftable_write_options *opts);
+
#define cl_failf(fmt, ...) do { \
char desc[4096]; \
snprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h}
2025-04-29 17:52 ` [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h} Seyi Kuforiji
@ 2025-04-29 23:04 ` Junio C Hamano
2025-05-02 9:57 ` Patrick Steinhardt
2025-05-02 9:57 ` Patrick Steinhardt
1 sibling, 1 reply; 31+ messages in thread
From: Junio C Hamano @ 2025-04-29 23:04 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, ps, phillip.wood
Seyi Kuforiji <kuforiji98@gmail.com> writes:
> Helper functions defined in `t/unit-tests/lib-reftable.{c,h}` are
> required for the reftable-related test files to run efficeintly. In the
efficeintly? effectively? efficiently? correctly? Wouldn't it be
sufficient to say "... to run." without anything else?
> current implementation these functions are designed to conform with our
> homegrown unit-testing structure. So in other to convert the reftable
> test files, there is need for a clar specific implementation of these
> helper functions.
OK.
> type cast `for (size_t i = 0; i < (size_t)stats->ref_stats.blocks;
> i++)`, implement equivalent helper functions in unit-test.{c,h} to use
> clar.
I cannot quite parse this.
> These functions conform with the clar testing framework and become
> available for all reftable-related test files implemented using the clar
> testing framework, which requires them. This will be used by subsequent
> commits.
OK.
> Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
> ---
> t/unit-tests/unit-test.c | 93 ++++++++++++++++++++++++++++++++++++++++
> t/unit-tests/unit-test.h | 16 +++++++
> 2 files changed, 109 insertions(+)
Hmph, this probably is a question better asked to Patrick, but it
somehow feels a bit unsatisfactory that we are duplicating instead
of replacing, as we cannot see "ah, this removed thing is now added
in a different shape to fit in the other framework" in the patch---
instead what we see in the patch is a bunch of "a completely new
thing that honors the convention we are familiar in existing clar
based tests is added here".
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h}
2025-04-29 23:04 ` Junio C Hamano
@ 2025-05-02 9:57 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Seyi Kuforiji, git, phillip.wood
On Tue, Apr 29, 2025 at 04:04:22PM -0700, Junio C Hamano wrote:
> Seyi Kuforiji <kuforiji98@gmail.com> writes:
> > Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
> > ---
> > t/unit-tests/unit-test.c | 93 ++++++++++++++++++++++++++++++++++++++++
> > t/unit-tests/unit-test.h | 16 +++++++
> > 2 files changed, 109 insertions(+)
>
> Hmph, this probably is a question better asked to Patrick, but it
> somehow feels a bit unsatisfactory that we are duplicating instead
> of replacing, as we cannot see "ah, this removed thing is now added
> in a different shape to fit in the other framework" in the patch---
> instead what we see in the patch is a bunch of "a completely new
> thing that honors the convention we are familiar in existing clar
> based tests is added here".
Well, my expectation is that at the end of this patch series we will
remove the old functionality. The steps should roughly be:
1. Implement equivalents for the reftable-specific test library for
clar.
2. Convert all reftable unit tests to use the new functions.
3. Remove the old functions.
Given that (2) is a bunch of tests it makes perfect sense to first have
a separate patch that adds without removing yet.
What I found a bit confusing though is that the new functions aren't
added to "t/unit-tests/lib-reftable.{c,h}". This would be very much
preferable from my point of view as the test functions aren't globally
relevant, but really only relevant to the reftable library.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h}
2025-04-29 17:52 ` [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h} Seyi Kuforiji
2025-04-29 23:04 ` Junio C Hamano
@ 2025-05-02 9:57 ` Patrick Steinhardt
1 sibling, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:52:53PM +0100, Seyi Kuforiji wrote:
> Helper functions defined in `t/unit-tests/lib-reftable.{c,h}` are
> required for the reftable-related test files to run efficeintly. In the
> current implementation these functions are designed to conform with our
> homegrown unit-testing structure. So in other to convert the reftable
> test files, there is need for a clar specific implementation of these
> helper functions.
>
> type cast `for (size_t i = 0; i < (size_t)stats->ref_stats.blocks;
> i++)`, implement equivalent helper functions in unit-test.{c,h} to use
> clar. These functions conform with the clar testing framework and become
> available for all reftable-related test files implemented using the clar
> testing framework, which requires them. This will be used by subsequent
> commits.
>
> Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
> ---
> t/unit-tests/unit-test.c | 93 ++++++++++++++++++++++++++++++++++++++++
> t/unit-tests/unit-test.h | 16 +++++++
> 2 files changed, 109 insertions(+)
I think this functionality should be added to
"t/unit-tests/lib-reftable.{c,h}" instead of to the generic unit testing
library as it is highly specific to reftables.
> diff --git a/t/unit-tests/unit-test.c b/t/unit-tests/unit-test.c
> index 5af645048a..6c2a4e6aa8 100644
> --- a/t/unit-tests/unit-test.c
> +++ b/t/unit-tests/unit-test.c
> @@ -1,10 +1,103 @@
> #include "unit-test.h"
> #include "hex.h"
> #include "parse-options.h"
> +#include "reftable/constants.h"
> +#include "reftable/writer.h"
> #include "strbuf.h"
> #include "string-list.h"
> #include "strvec.h"
>
> +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id)
> +{
> + memset(p, (uint8_t)i, hash_size(id));
> +}
> +
> +static ssize_t strbuf_writer_write(void *b, const void *data, size_t sz)
> +{
> + strbuf_add(b, data, sz);
> + return sz;
> +}
> +
> +static int strbuf_writer_flush(void *arg UNUSED)
> +{
> + return 0;
> +}
> +
> +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
> + struct reftable_write_options *opts)
> +{
> + struct reftable_writer *writer;
> + int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush,
> + buf, opts);
> + cl_assert(ret == 0);
We typically don't explicitly compare with zero, so this should rather
be `cl_assert(!ret)`.
> + return writer;
> +}
> +
> +void cl_reftable_write_to_buf(struct reftable_buf *buf,
> + struct reftable_ref_record *refs,
> + size_t nrefs,
> + struct reftable_log_record *logs,
> + size_t nlogs,
> + struct reftable_write_options *_opts)
> +{
> + struct reftable_write_options opts = { 0 };
> + const struct reftable_stats *stats;
> + struct reftable_writer *writer;
> + uint64_t min = 0xffffffff;
> + uint64_t max = 0;
> + int ret;
> +
> + if (_opts)
> + opts = *_opts;
> +
> + for (size_t i = 0; i < nrefs; i++) {
> + uint64_t ui = refs[i].update_index;
> + if (ui > max)
> + max = ui;
> + if (ui < min)
> + min = ui;
> + }
> + for (size_t i = 0; i < nlogs; i++) {
> + uint64_t ui = logs[i].update_index;
> + if (ui > max)
> + max = ui;
> + if (ui < min)
> + min = ui;
> + }
> +
> + writer = cl_reftable_strbuf_writer(buf, &opts);
> + reftable_writer_set_limits(writer, min, max);
This function may return an error, as well, so let's verify it while at
it.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 02/10] t/unit-tests: convert reftable basics test to use clar test framework
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
2025-04-29 17:52 ` [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h} Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:52 ` [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar Seyi Kuforiji
` (7 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable basics test file to clar by using clar assertions
where necessary.Break up test edge case to improve modularity and
clarity.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-basics.c | 219 -------------------------------
t/unit-tests/u-reftable-basics.c | 195 +++++++++++++++++++++++++++
4 files changed, 197 insertions(+), 221 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-basics.c
create mode 100644 t/unit-tests/u-reftable-basics.c
diff --git a/Makefile b/Makefile
index 13f9062a05..7b12bb078c 100644
--- a/Makefile
+++ b/Makefile
@@ -1362,6 +1362,7 @@ CLAR_TEST_SUITES += u-oid-array
CLAR_TEST_SUITES += u-oidmap
CLAR_TEST_SUITES += u-oidtree
CLAR_TEST_SUITES += u-prio-queue
+CLAR_TEST_SUITES += u-reftable-basics
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1374,7 +1375,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-basics
UNIT_TEST_PROGRAMS += t-reftable-block
UNIT_TEST_PROGRAMS += t-reftable-merged
UNIT_TEST_PROGRAMS += t-reftable-pq
diff --git a/t/meson.build b/t/meson.build
index bfb744e886..8a42b595d9 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -8,6 +8,7 @@ clar_test_suites = [
'unit-tests/u-oidmap.c',
'unit-tests/u-oidtree.c',
'unit-tests/u-prio-queue.c',
+ 'unit-tests/u-reftable-basics.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -54,7 +55,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-basics.c',
'unit-tests/t-reftable-block.c',
'unit-tests/t-reftable-merged.c',
'unit-tests/t-reftable-pq.c',
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
deleted file mode 100644
index c9e751e49e..0000000000
--- a/t/unit-tests/t-reftable-basics.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
-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/basics.h"
-
-struct integer_needle_lesseq_args {
- int needle;
- int *haystack;
-};
-
-static int integer_needle_lesseq(size_t i, void *_args)
-{
- struct integer_needle_lesseq_args *args = _args;
- return args->needle <= args->haystack[i];
-}
-
-static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
-{
- return NULL;
-}
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- if_test ("binary search with binsearch works") {
- int haystack[] = { 2, 4, 6, 8, 10 };
- struct {
- int needle;
- size_t expected_idx;
- } testcases[] = {
- {-9000, 0},
- {-1, 0},
- {0, 0},
- {2, 0},
- {3, 1},
- {4, 1},
- {7, 3},
- {9, 4},
- {10, 4},
- {11, 5},
- {9000, 5},
- };
-
- for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
- struct integer_needle_lesseq_args args = {
- .haystack = haystack,
- .needle = testcases[i].needle,
- };
- size_t idx;
-
- idx = binsearch(ARRAY_SIZE(haystack),
- &integer_needle_lesseq, &args);
- check_int(idx, ==, testcases[i].expected_idx);
- }
- }
-
- if_test ("names_length returns size of a NULL-terminated string array") {
- const char *a[] = { "a", "b", NULL };
- check_int(names_length(a), ==, 2);
- }
-
- if_test ("names_equal compares NULL-terminated string arrays") {
- const char *a[] = { "a", "b", "c", NULL };
- const char *b[] = { "a", "b", "d", NULL };
- const char *c[] = { "a", "b", NULL };
-
- check(names_equal(a, a));
- check(!names_equal(a, b));
- check(!names_equal(a, c));
- }
-
- if_test ("parse_names works for basic input") {
- char in1[] = "line\n";
- char in2[] = "a\nb\nc";
- char **out = parse_names(in1, strlen(in1));
- check(out != NULL);
- check_str(out[0], "line");
- check(!out[1]);
- free_names(out);
-
- out = parse_names(in2, strlen(in2));
- check(out != NULL);
- check_str(out[0], "a");
- check_str(out[1], "b");
- check_str(out[2], "c");
- check(!out[3]);
- free_names(out);
- }
-
- if_test ("parse_names drops empty string") {
- char in[] = "a\n\nb\n";
- char **out = parse_names(in, strlen(in));
- check(out != NULL);
- check_str(out[0], "a");
- /* simply '\n' should be dropped as empty string */
- check_str(out[1], "b");
- check(!out[2]);
- free_names(out);
- }
-
- if_test ("common_prefix_size works") {
- struct reftable_buf a = REFTABLE_BUF_INIT;
- struct reftable_buf b = REFTABLE_BUF_INIT;
- struct {
- const char *a, *b;
- int want;
- } cases[] = {
- {"abcdef", "abc", 3},
- { "abc", "ab", 2 },
- { "", "abc", 0 },
- { "abc", "abd", 2 },
- { "abc", "pqr", 0 },
- };
-
- for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
- check(!reftable_buf_addstr(&a, cases[i].a));
- check(!reftable_buf_addstr(&b, cases[i].b));
- check_uint(common_prefix_size(&a, &b), ==, cases[i].want);
- reftable_buf_reset(&a);
- reftable_buf_reset(&b);
- }
- reftable_buf_release(&a);
- reftable_buf_release(&b);
- }
-
- if_test ("reftable_put_be64 and reftable_get_be64 work") {
- uint64_t in = 0x1122334455667788;
- uint8_t dest[8];
- uint64_t out;
- reftable_put_be64(dest, in);
- out = reftable_get_be64(dest);
- check_int(in, ==, out);
- }
-
- if_test ("reftable_put_be32 and reftable_get_be32 work") {
- uint32_t in = 0x11223344;
- uint8_t dest[4];
- uint32_t out;
- reftable_put_be32(dest, in);
- out = reftable_get_be32(dest);
- check_int(in, ==, out);
- }
-
- if_test ("reftable_put_be24 and reftable_get_be24 work") {
- uint32_t in = 0x112233;
- uint8_t dest[3];
- uint32_t out;
- reftable_put_be24(dest, in);
- out = reftable_get_be24(dest);
- check_int(in, ==, out);
- }
-
- if_test ("put_be16 and get_be16 work") {
- uint32_t in = 0xfef1;
- uint8_t dest[3];
- uint32_t out;
- reftable_put_be16(dest, in);
- out = reftable_get_be16(dest);
- check_int(in, ==, out);
- }
-
- if_test ("REFTABLE_ALLOC_GROW works") {
- int *arr = NULL, *old_arr;
- size_t alloc = 0, old_alloc;
-
- check(!REFTABLE_ALLOC_GROW(arr, 1, alloc));
- check(arr != NULL);
- check_uint(alloc, >=, 1);
- arr[0] = 42;
-
- old_alloc = alloc;
- old_arr = arr;
- reftable_set_alloc(NULL, realloc_stub, NULL);
- check(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
- check(arr == old_arr);
- check_uint(alloc, ==, old_alloc);
-
- old_alloc = alloc;
- reftable_set_alloc(NULL, NULL, NULL);
- check(!REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
- check(arr != NULL);
- check_uint(alloc, >, old_alloc);
- arr[alloc - 1] = 42;
-
- reftable_free(arr);
- }
-
- if_test ("REFTABLE_ALLOC_GROW_OR_NULL works") {
- int *arr = NULL;
- size_t alloc = 0, old_alloc;
-
- REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
- check(arr != NULL);
- check_uint(alloc, >=, 1);
- arr[0] = 42;
-
- old_alloc = alloc;
- REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
- check(arr != NULL);
- check_uint(alloc, >, old_alloc);
- arr[alloc - 1] = 42;
-
- old_alloc = alloc;
- reftable_set_alloc(NULL, realloc_stub, NULL);
- REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
- check(arr == NULL);
- check_uint(alloc, ==, 0);
- reftable_set_alloc(NULL, NULL, NULL);
-
- reftable_free(arr);
- }
-
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-basics.c b/t/unit-tests/u-reftable-basics.c
new file mode 100644
index 0000000000..63dd568faf
--- /dev/null
+++ b/t/unit-tests/u-reftable-basics.c
@@ -0,0 +1,195 @@
+/*
+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 "unit-test.h"
+#include "reftable/basics.h"
+
+struct integer_needle_lesseq_args {
+ int needle;
+ int *haystack;
+};
+
+static int integer_needle_lesseq(size_t i, void *_args)
+{
+ struct integer_needle_lesseq_args *args = _args;
+ return args->needle <= args->haystack[i];
+}
+
+static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
+{
+ return NULL;
+}
+
+void test_reftable_basics__binsearch(void)
+{
+ int haystack[] = { 2, 4, 6, 8, 10 };
+ struct {
+ int needle;
+ size_t expected_idx;
+ } testcases[] = {
+ {-9000, 0},
+ {-1, 0},
+ {0, 0},
+ {2, 0},
+ {3, 1},
+ {4, 1},
+ {7, 3},
+ {9, 4},
+ {10, 4},
+ {11, 5},
+ {9000, 5},
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+ struct integer_needle_lesseq_args args = {
+ .haystack = haystack,
+ .needle = testcases[i].needle,
+ };
+ size_t idx;
+
+ idx = binsearch(ARRAY_SIZE(haystack),
+ &integer_needle_lesseq, &args);
+ cl_assert_equal_i(idx, testcases[i].expected_idx);
+ }
+
+}
+
+void test_reftable_basics__names_length(void)
+{
+ const char *a[] = { "a", "b", NULL };
+ cl_assert_equal_i(names_length(a), 2);
+}
+
+void test_reftable_basics__names_equal(void)
+{
+ const char *a[] = { "a", "b", "c", NULL };
+ const char *b[] = { "a", "b", "d", NULL };
+ const char *c[] = { "a", "b", NULL };
+
+ cl_assert(names_equal(a, a));
+ cl_assert(!names_equal(a, b));
+ cl_assert(!names_equal(a, c));
+}
+
+void test_reftable_basics__parse_names(void)
+{
+ char in1[] = "line\n";
+ char in2[] = "a\nb\nc";
+ char **out = parse_names(in1, strlen(in1));
+ cl_assert(out != NULL);
+ cl_assert_equal_s(out[0], "line");
+ cl_assert(!out[1]);
+ free_names(out);
+
+ out = parse_names(in2, strlen(in2));
+ cl_assert(out != NULL);
+ cl_assert_equal_s(out[0], "a");
+ cl_assert_equal_s(out[1], "b");
+ cl_assert_equal_s(out[2], "c");
+ cl_assert(!out[3]);
+ free_names(out);
+}
+
+void test_reftable_basics__common_prefix_size(void)
+{
+ struct reftable_buf a = REFTABLE_BUF_INIT;
+ struct reftable_buf b = REFTABLE_BUF_INIT;
+ struct {
+ const char *a, *b;
+ int want;
+ } cases[] = {
+ {"abcdef", "abc", 3},
+ { "abc", "ab", 2 },
+ { "", "abc", 0 },
+ { "abc", "abd", 2 },
+ { "abc", "pqr", 0 },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
+ reftable_buf_reset(&a);
+ reftable_buf_reset(&b);
+ cl_assert_equal_i(reftable_buf_addstr(&a, cases[i].a), 0);
+ cl_assert_equal_i(reftable_buf_addstr(&b, cases[i].b), 0);
+ cl_assert_equal_i(common_prefix_size(&a, &b), cases[i].want);
+ }
+ reftable_buf_release(&a);
+ reftable_buf_release(&b);
+}
+
+void test_reftable_basics__put_get_be24(void)
+{
+ uint32_t in = 0x112233;
+ uint8_t dest[3];
+ uint32_t out;
+ reftable_put_be24(dest, in);
+ out = reftable_get_be24(dest);
+ cl_assert_equal_i(in, out);
+}
+
+void test_reftable_basics__put_get_be16(void)
+{
+ uint32_t in = 0xfef1;
+ uint8_t dest[3];
+ uint32_t out;
+ reftable_put_be16(dest, in);
+ out = reftable_get_be16(dest);
+ cl_assert_equal_i(in, out);
+}
+
+void test_reftable_basics__grow_alloc(void)
+{
+ int *arr = NULL, *old_arr;
+ size_t alloc = 0, old_alloc;
+
+ cl_assert_equal_i(REFTABLE_ALLOC_GROW(arr, 1, alloc), 0);
+ cl_assert(arr != NULL);
+ cl_assert(alloc >= 1);
+ arr[0] = 42;
+
+ old_alloc = alloc;
+ old_arr = arr;
+ reftable_set_alloc(NULL, realloc_stub, NULL);
+ cl_assert(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
+ cl_assert(arr == old_arr);
+ cl_assert_equal_i(alloc, old_alloc);
+
+ old_alloc = alloc;
+ reftable_set_alloc(NULL, NULL, NULL);
+ cl_assert_equal_i(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc), 0);
+ cl_assert(arr != NULL);
+ cl_assert(alloc > old_alloc);
+ arr[alloc - 1] = 42;
+
+ reftable_free(arr);
+}
+
+void test_reftable_basics__grow_alloc_or_null(void)
+{
+ int *arr = NULL;
+ size_t alloc = 0, old_alloc;
+
+ REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
+ cl_assert(arr != NULL);
+ cl_assert(alloc >= 1);
+ arr[0] = 42;
+
+ old_alloc = alloc;
+ REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
+ cl_assert(arr != NULL);
+ cl_assert(alloc > old_alloc);
+ arr[alloc - 1] = 42;
+
+ old_alloc = alloc;
+ reftable_set_alloc(NULL, realloc_stub, NULL);
+ REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
+ cl_assert(arr == NULL);
+ cl_assert_equal_i(alloc, 0);
+ reftable_set_alloc(NULL, NULL, NULL);
+
+ reftable_free(arr);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 02/10] t/unit-tests: convert reftable basics test to use clar test framework
2025-04-29 17:52 ` [PATCH v2 02/10] t/unit-tests: convert reftable basics test to use clar test framework Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:52:54PM +0100, Seyi Kuforiji wrote:
> Adapt reftable basics test file to clar by using clar assertions
> where necessary.Break up test edge case to improve modularity and
> clarity.
>
> Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
> ---
> Makefile | 2 +-
> t/meson.build | 2 +-
> t/unit-tests/t-reftable-basics.c | 219 -------------------------------
> t/unit-tests/u-reftable-basics.c | 195 +++++++++++++++++++++++++++
> 4 files changed, 197 insertions(+), 221 deletions(-)
> delete mode 100644 t/unit-tests/t-reftable-basics.c
> create mode 100644 t/unit-tests/u-reftable-basics.c
>
> diff --git a/Makefile b/Makefile
> index 13f9062a05..7b12bb078c 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1362,6 +1362,7 @@ CLAR_TEST_SUITES += u-oid-array
> CLAR_TEST_SUITES += u-oidmap
> CLAR_TEST_SUITES += u-oidtree
> CLAR_TEST_SUITES += u-prio-queue
> +CLAR_TEST_SUITES += u-reftable-basics
> CLAR_TEST_SUITES += u-reftable-tree
> CLAR_TEST_SUITES += u-strbuf
> CLAR_TEST_SUITES += u-strcmp-offset
> @@ -1374,7 +1375,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
> CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
> CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
>
> -UNIT_TEST_PROGRAMS += t-reftable-basics
> UNIT_TEST_PROGRAMS += t-reftable-block
> UNIT_TEST_PROGRAMS += t-reftable-merged
> UNIT_TEST_PROGRAMS += t-reftable-pq
> diff --git a/t/meson.build b/t/meson.build
> index bfb744e886..8a42b595d9 100644
> --- a/t/meson.build
> +++ b/t/meson.build
> @@ -8,6 +8,7 @@ clar_test_suites = [
> 'unit-tests/u-oidmap.c',
> 'unit-tests/u-oidtree.c',
> 'unit-tests/u-prio-queue.c',
> + 'unit-tests/u-reftable-basics.c',
> 'unit-tests/u-reftable-tree.c',
> 'unit-tests/u-strbuf.c',
> 'unit-tests/u-strcmp-offset.c',
> @@ -54,7 +55,6 @@ clar_unit_tests = executable('unit-tests',
> test('unit-tests', clar_unit_tests)
>
> unit_test_programs = [
> - 'unit-tests/t-reftable-basics.c',
> 'unit-tests/t-reftable-block.c',
> 'unit-tests/t-reftable-merged.c',
> 'unit-tests/t-reftable-pq.c',
> diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
> deleted file mode 100644
> index c9e751e49e..0000000000
> --- a/t/unit-tests/t-reftable-basics.c
> +++ /dev/null
> @@ -1,219 +0,0 @@
> -/*
> -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/basics.h"
> -
> -struct integer_needle_lesseq_args {
> - int needle;
> - int *haystack;
> -};
> -
> -static int integer_needle_lesseq(size_t i, void *_args)
> -{
> - struct integer_needle_lesseq_args *args = _args;
> - return args->needle <= args->haystack[i];
> -}
> -
> -static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
> -{
> - return NULL;
> -}
> -
> -int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
> -{
> - if_test ("binary search with binsearch works") {
> - int haystack[] = { 2, 4, 6, 8, 10 };
> - struct {
> - int needle;
> - size_t expected_idx;
> - } testcases[] = {
> - {-9000, 0},
> - {-1, 0},
> - {0, 0},
> - {2, 0},
> - {3, 1},
> - {4, 1},
> - {7, 3},
> - {9, 4},
> - {10, 4},
> - {11, 5},
> - {9000, 5},
> - };
> -
> - for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
> - struct integer_needle_lesseq_args args = {
> - .haystack = haystack,
> - .needle = testcases[i].needle,
> - };
> - size_t idx;
> -
> - idx = binsearch(ARRAY_SIZE(haystack),
> - &integer_needle_lesseq, &args);
> - check_int(idx, ==, testcases[i].expected_idx);
> - }
> - }
> -
> - if_test ("names_length returns size of a NULL-terminated string array") {
> - const char *a[] = { "a", "b", NULL };
> - check_int(names_length(a), ==, 2);
> - }
> -
> - if_test ("names_equal compares NULL-terminated string arrays") {
> - const char *a[] = { "a", "b", "c", NULL };
> - const char *b[] = { "a", "b", "d", NULL };
> - const char *c[] = { "a", "b", NULL };
> -
> - check(names_equal(a, a));
> - check(!names_equal(a, b));
> - check(!names_equal(a, c));
> - }
> -
> - if_test ("parse_names works for basic input") {
> - char in1[] = "line\n";
> - char in2[] = "a\nb\nc";
> - char **out = parse_names(in1, strlen(in1));
> - check(out != NULL);
> - check_str(out[0], "line");
> - check(!out[1]);
> - free_names(out);
> -
> - out = parse_names(in2, strlen(in2));
> - check(out != NULL);
> - check_str(out[0], "a");
> - check_str(out[1], "b");
> - check_str(out[2], "c");
> - check(!out[3]);
> - free_names(out);
> - }
> -
> - if_test ("parse_names drops empty string") {
> - char in[] = "a\n\nb\n";
> - char **out = parse_names(in, strlen(in));
> - check(out != NULL);
> - check_str(out[0], "a");
> - /* simply '\n' should be dropped as empty string */
> - check_str(out[1], "b");
> - check(!out[2]);
> - free_names(out);
> - }
> -
> - if_test ("common_prefix_size works") {
> - struct reftable_buf a = REFTABLE_BUF_INIT;
> - struct reftable_buf b = REFTABLE_BUF_INIT;
> - struct {
> - const char *a, *b;
> - int want;
> - } cases[] = {
> - {"abcdef", "abc", 3},
> - { "abc", "ab", 2 },
> - { "", "abc", 0 },
> - { "abc", "abd", 2 },
> - { "abc", "pqr", 0 },
> - };
> -
> - for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
> - check(!reftable_buf_addstr(&a, cases[i].a));
> - check(!reftable_buf_addstr(&b, cases[i].b));
> - check_uint(common_prefix_size(&a, &b), ==, cases[i].want);
> - reftable_buf_reset(&a);
> - reftable_buf_reset(&b);
> - }
> - reftable_buf_release(&a);
> - reftable_buf_release(&b);
> - }
> -
> - if_test ("reftable_put_be64 and reftable_get_be64 work") {
> - uint64_t in = 0x1122334455667788;
> - uint8_t dest[8];
> - uint64_t out;
> - reftable_put_be64(dest, in);
> - out = reftable_get_be64(dest);
> - check_int(in, ==, out);
> - }
> -
> - if_test ("reftable_put_be32 and reftable_get_be32 work") {
> - uint32_t in = 0x11223344;
> - uint8_t dest[4];
> - uint32_t out;
> - reftable_put_be32(dest, in);
> - out = reftable_get_be32(dest);
> - check_int(in, ==, out);
> - }
> -
> - if_test ("reftable_put_be24 and reftable_get_be24 work") {
> - uint32_t in = 0x112233;
> - uint8_t dest[3];
> - uint32_t out;
> - reftable_put_be24(dest, in);
> - out = reftable_get_be24(dest);
> - check_int(in, ==, out);
> - }
> -
> - if_test ("put_be16 and get_be16 work") {
> - uint32_t in = 0xfef1;
> - uint8_t dest[3];
> - uint32_t out;
> - reftable_put_be16(dest, in);
> - out = reftable_get_be16(dest);
> - check_int(in, ==, out);
> - }
> -
> - if_test ("REFTABLE_ALLOC_GROW works") {
> - int *arr = NULL, *old_arr;
> - size_t alloc = 0, old_alloc;
> -
> - check(!REFTABLE_ALLOC_GROW(arr, 1, alloc));
> - check(arr != NULL);
> - check_uint(alloc, >=, 1);
> - arr[0] = 42;
> -
> - old_alloc = alloc;
> - old_arr = arr;
> - reftable_set_alloc(NULL, realloc_stub, NULL);
> - check(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
> - check(arr == old_arr);
> - check_uint(alloc, ==, old_alloc);
> -
> - old_alloc = alloc;
> - reftable_set_alloc(NULL, NULL, NULL);
> - check(!REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
> - check(arr != NULL);
> - check_uint(alloc, >, old_alloc);
> - arr[alloc - 1] = 42;
> -
> - reftable_free(arr);
> - }
> -
> - if_test ("REFTABLE_ALLOC_GROW_OR_NULL works") {
> - int *arr = NULL;
> - size_t alloc = 0, old_alloc;
> -
> - REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
> - check(arr != NULL);
> - check_uint(alloc, >=, 1);
> - arr[0] = 42;
> -
> - old_alloc = alloc;
> - REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
> - check(arr != NULL);
> - check_uint(alloc, >, old_alloc);
> - arr[alloc - 1] = 42;
> -
> - old_alloc = alloc;
> - reftable_set_alloc(NULL, realloc_stub, NULL);
> - REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
> - check(arr == NULL);
> - check_uint(alloc, ==, 0);
> - reftable_set_alloc(NULL, NULL, NULL);
> -
> - reftable_free(arr);
> - }
> -
> - return test_done();
> -}
> diff --git a/t/unit-tests/u-reftable-basics.c b/t/unit-tests/u-reftable-basics.c
> new file mode 100644
> index 0000000000..63dd568faf
> --- /dev/null
> +++ b/t/unit-tests/u-reftable-basics.c
> @@ -0,0 +1,195 @@
> +/*
> +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 "unit-test.h"
> +#include "reftable/basics.h"
> +
> +struct integer_needle_lesseq_args {
> + int needle;
> + int *haystack;
> +};
> +
> +static int integer_needle_lesseq(size_t i, void *_args)
> +{
> + struct integer_needle_lesseq_args *args = _args;
> + return args->needle <= args->haystack[i];
> +}
> +
> +static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
> +{
> + return NULL;
> +}
> +
> +void test_reftable_basics__binsearch(void)
> +{
> + int haystack[] = { 2, 4, 6, 8, 10 };
> + struct {
> + int needle;
> + size_t expected_idx;
> + } testcases[] = {
> + {-9000, 0},
> + {-1, 0},
> + {0, 0},
> + {2, 0},
> + {3, 1},
> + {4, 1},
> + {7, 3},
> + {9, 4},
> + {10, 4},
> + {11, 5},
> + {9000, 5},
> + };
> +
> + for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
> + struct integer_needle_lesseq_args args = {
> + .haystack = haystack,
> + .needle = testcases[i].needle,
> + };
> + size_t idx;
> +
> + idx = binsearch(ARRAY_SIZE(haystack),
> + &integer_needle_lesseq, &args);
> + cl_assert_equal_i(idx, testcases[i].expected_idx);
> + }
> +
Nit: empty newline can be removed.
> +}
> +
> +void test_reftable_basics__names_length(void)
> +{
> + const char *a[] = { "a", "b", NULL };
> + cl_assert_equal_i(names_length(a), 2);
> +}
> +
> +void test_reftable_basics__names_equal(void)
> +{
> + const char *a[] = { "a", "b", "c", NULL };
> + const char *b[] = { "a", "b", "d", NULL };
> + const char *c[] = { "a", "b", NULL };
> +
> + cl_assert(names_equal(a, a));
> + cl_assert(!names_equal(a, b));
> + cl_assert(!names_equal(a, c));
> +}
> +
> +void test_reftable_basics__parse_names(void)
> +{
> + char in1[] = "line\n";
> + char in2[] = "a\nb\nc";
> + char **out = parse_names(in1, strlen(in1));
> + cl_assert(out != NULL);
> + cl_assert_equal_s(out[0], "line");
> + cl_assert(!out[1]);
> + free_names(out);
> +
> + out = parse_names(in2, strlen(in2));
> + cl_assert(out != NULL);
> + cl_assert_equal_s(out[0], "a");
> + cl_assert_equal_s(out[1], "b");
> + cl_assert_equal_s(out[2], "c");
> + cl_assert(!out[3]);
> + free_names(out);
> +}
> +
I think you missed converting "parse_names drops empty string".
> +void test_reftable_basics__common_prefix_size(void)
> +{
> + struct reftable_buf a = REFTABLE_BUF_INIT;
> + struct reftable_buf b = REFTABLE_BUF_INIT;
> + struct {
> + const char *a, *b;
> + int want;
> + } cases[] = {
> + {"abcdef", "abc", 3},
> + { "abc", "ab", 2 },
> + { "", "abc", 0 },
> + { "abc", "abd", 2 },
> + { "abc", "pqr", 0 },
> + };
> +
> + for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
> + reftable_buf_reset(&a);
> + reftable_buf_reset(&b);
> + cl_assert_equal_i(reftable_buf_addstr(&a, cases[i].a), 0);
> + cl_assert_equal_i(reftable_buf_addstr(&b, cases[i].b), 0);
> + cl_assert_equal_i(common_prefix_size(&a, &b), cases[i].want);
Why did you change the order of `reftable_buf_reset()` calls? The
reordered logic achieves the same result, but I'd recommend to keep
things as-is so that reviewers aren't puzzled by this arbitrary change.
> + }
> + reftable_buf_release(&a);
> + reftable_buf_release(&b);
> +}
I miss tests for `put_be64` and `put_be32`.
> +void test_reftable_basics__put_get_be24(void)
> +{
> + uint32_t in = 0x112233;
> + uint8_t dest[3];
> + uint32_t out;
> + reftable_put_be24(dest, in);
> + out = reftable_get_be24(dest);
> + cl_assert_equal_i(in, out);
> +}
> +
> +void test_reftable_basics__put_get_be16(void)
> +{
> + uint32_t in = 0xfef1;
> + uint8_t dest[3];
> + uint32_t out;
> + reftable_put_be16(dest, in);
> + out = reftable_get_be16(dest);
> + cl_assert_equal_i(in, out);
> +}
> +
> +void test_reftable_basics__grow_alloc(void)
Let's rename this to `__alloc_grow()` to match the name of the function.
> +{
> + int *arr = NULL, *old_arr;
> + size_t alloc = 0, old_alloc;
> +
> + cl_assert_equal_i(REFTABLE_ALLOC_GROW(arr, 1, alloc), 0);
> + cl_assert(arr != NULL);
> + cl_assert(alloc >= 1);
> + arr[0] = 42;
> +
> + old_alloc = alloc;
> + old_arr = arr;
> + reftable_set_alloc(NULL, realloc_stub, NULL);
> + cl_assert(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
> + cl_assert(arr == old_arr);
> + cl_assert_equal_i(alloc, old_alloc);
> +
> + old_alloc = alloc;
> + reftable_set_alloc(NULL, NULL, NULL);
> + cl_assert_equal_i(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc), 0);
> + cl_assert(arr != NULL);
> + cl_assert(alloc > old_alloc);
> + arr[alloc - 1] = 42;
> +
> + reftable_free(arr);
> +}
> +
> +void test_reftable_basics__grow_alloc_or_null(void)
Same here, let's rename to `alloc_grow_or_null`.
> +{
> + int *arr = NULL;
> + size_t alloc = 0, old_alloc;
> +
> + REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
> + cl_assert(arr != NULL);
> + cl_assert(alloc >= 1);
> + arr[0] = 42;
> +
> + old_alloc = alloc;
> + REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
> + cl_assert(arr != NULL);
> + cl_assert(alloc > old_alloc);
> + arr[alloc - 1] = 42;
> +
> + old_alloc = alloc;
> + reftable_set_alloc(NULL, realloc_stub, NULL);
> + REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
> + cl_assert(arr == NULL);
> + cl_assert_equal_i(alloc, 0);
> + reftable_set_alloc(NULL, NULL, NULL);
> +
> + reftable_free(arr);
> +}
Other than that this looks well-done to me, thanks!
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
2025-04-29 17:52 ` [PATCH v2 01/10] t/unit-tests: implement reftable test helper functions in unit-test.{c,h} Seyi Kuforiji
2025-04-29 17:52 ` [PATCH v2 02/10] t/unit-tests: convert reftable basics test to use clar test framework Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:52 ` [PATCH v2 04/10] t/unit-tests: convert reftable merged " Seyi Kuforiji
` (6 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable block test file to use clar testing framework by using
clar assertions where necessary.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-block.c | 383 --------------------------------
t/unit-tests/u-reftable-block.c | 373 +++++++++++++++++++++++++++++++
4 files changed, 375 insertions(+), 385 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-block.c
create mode 100644 t/unit-tests/u-reftable-block.c
diff --git a/Makefile b/Makefile
index 7b12bb078c..239c575dad 100644
--- a/Makefile
+++ b/Makefile
@@ -1363,6 +1363,7 @@ CLAR_TEST_SUITES += u-oidmap
CLAR_TEST_SUITES += u-oidtree
CLAR_TEST_SUITES += u-prio-queue
CLAR_TEST_SUITES += u-reftable-basics
+CLAR_TEST_SUITES += u-reftable-block
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1375,7 +1376,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-block
UNIT_TEST_PROGRAMS += t-reftable-merged
UNIT_TEST_PROGRAMS += t-reftable-pq
UNIT_TEST_PROGRAMS += t-reftable-reader
diff --git a/t/meson.build b/t/meson.build
index 8a42b595d9..e0d723e74b 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -9,6 +9,7 @@ clar_test_suites = [
'unit-tests/u-oidtree.c',
'unit-tests/u-prio-queue.c',
'unit-tests/u-reftable-basics.c',
+ 'unit-tests/u-reftable-block.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -55,7 +56,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-block.c',
'unit-tests/t-reftable-merged.c',
'unit-tests/t-reftable-pq.c',
'unit-tests/t-reftable-reader.c',
diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
deleted file mode 100644
index 22040aeefa..0000000000
--- a/t/unit-tests/t-reftable-block.c
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
-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/block.h"
-#include "reftable/blocksource.h"
-#include "reftable/constants.h"
-#include "reftable/reftable-error.h"
-#include "strbuf.h"
-
-static void t_ref_block_read_write(void)
-{
- const int header_off = 21; /* random */
- struct reftable_record recs[30];
- const size_t N = ARRAY_SIZE(recs);
- const size_t block_size = 1024;
- struct reftable_block block = { 0 };
- struct block_writer bw = {
- .last_key = REFTABLE_BUF_INIT,
- };
- struct reftable_record rec = {
- .type = BLOCK_TYPE_REF,
- };
- size_t i = 0;
- int ret;
- struct block_reader br = { 0 };
- struct block_iter it = BLOCK_ITER_INIT;
- struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
-
- REFTABLE_CALLOC_ARRAY(block.data, block_size);
- check(block.data != NULL);
- block.len = block_size;
- block_source_from_buf(&block.source ,&buf);
- ret = block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
- header_off, hash_size(REFTABLE_HASH_SHA1));
- check(!ret);
-
- rec.u.ref.refname = (char *) "";
- rec.u.ref.value_type = REFTABLE_REF_DELETION;
- ret = block_writer_add(&bw, &rec);
- check_int(ret, ==, REFTABLE_API_ERROR);
-
- for (i = 0; i < N; i++) {
- rec.u.ref.refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
- rec.u.ref.value_type = REFTABLE_REF_VAL1;
- memset(rec.u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1);
-
- recs[i] = rec;
- ret = block_writer_add(&bw, &rec);
- rec.u.ref.refname = NULL;
- rec.u.ref.value_type = REFTABLE_REF_DELETION;
- check_int(ret, ==, 0);
- }
-
- ret = block_writer_finish(&bw);
- check_int(ret, >, 0);
-
- block_writer_release(&bw);
-
- block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
-
- block_iter_seek_start(&it, &br);
-
- for (i = 0; ; i++) {
- ret = block_iter_next(&it, &rec);
- check_int(ret, >=, 0);
- if (ret > 0) {
- check_int(i, ==, N);
- break;
- }
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- for (i = 0; i < N; i++) {
- block_iter_reset(&it);
- reftable_record_key(&recs[i], &want);
-
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
-
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
-
- want.len--;
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
- check(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- block_reader_release(&br);
- block_iter_close(&it);
- reftable_record_release(&rec);
- reftable_block_done(&br.block);
- reftable_buf_release(&want);
- reftable_buf_release(&buf);
- for (i = 0; i < N; i++)
- reftable_record_release(&recs[i]);
-}
-
-static void t_log_block_read_write(void)
-{
- const int header_off = 21;
- struct reftable_record recs[30];
- const size_t N = ARRAY_SIZE(recs);
- const size_t block_size = 2048;
- struct reftable_block block = { 0 };
- struct block_writer bw = {
- .last_key = REFTABLE_BUF_INIT,
- };
- struct reftable_record rec = {
- .type = BLOCK_TYPE_LOG,
- };
- size_t i = 0;
- int ret;
- struct block_reader br = { 0 };
- struct block_iter it = BLOCK_ITER_INIT;
- struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
-
- REFTABLE_CALLOC_ARRAY(block.data, block_size);
- check(block.data != NULL);
- block.len = block_size;
- block_source_from_buf(&block.source ,&buf);
- ret = block_writer_init(&bw, BLOCK_TYPE_LOG, block.data, block_size,
- header_off, hash_size(REFTABLE_HASH_SHA1));
- check(!ret);
-
- for (i = 0; i < N; i++) {
- rec.u.log.refname = xstrfmt("branch%02"PRIuMAX , (uintmax_t)i);
- rec.u.log.update_index = i;
- rec.u.log.value_type = REFTABLE_LOG_UPDATE;
-
- recs[i] = rec;
- ret = block_writer_add(&bw, &rec);
- rec.u.log.refname = NULL;
- rec.u.log.value_type = REFTABLE_LOG_DELETION;
- check_int(ret, ==, 0);
- }
-
- ret = block_writer_finish(&bw);
- check_int(ret, >, 0);
-
- block_writer_release(&bw);
-
- block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
-
- block_iter_seek_start(&it, &br);
-
- for (i = 0; ; i++) {
- ret = block_iter_next(&it, &rec);
- check_int(ret, >=, 0);
- if (ret > 0) {
- check_int(i, ==, N);
- break;
- }
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- for (i = 0; i < N; i++) {
- block_iter_reset(&it);
- reftable_buf_reset(&want);
- check(!reftable_buf_addstr(&want, recs[i].u.log.refname));
-
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
-
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
-
- want.len--;
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
- check(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- block_reader_release(&br);
- block_iter_close(&it);
- reftable_record_release(&rec);
- reftable_block_done(&br.block);
- reftable_buf_release(&want);
- reftable_buf_release(&buf);
- for (i = 0; i < N; i++)
- reftable_record_release(&recs[i]);
-}
-
-static void t_obj_block_read_write(void)
-{
- const int header_off = 21;
- struct reftable_record recs[30];
- const size_t N = ARRAY_SIZE(recs);
- const size_t block_size = 1024;
- struct reftable_block block = { 0 };
- struct block_writer bw = {
- .last_key = REFTABLE_BUF_INIT,
- };
- struct reftable_record rec = {
- .type = BLOCK_TYPE_OBJ,
- };
- size_t i = 0;
- int ret;
- struct block_reader br = { 0 };
- struct block_iter it = BLOCK_ITER_INIT;
- struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
-
- REFTABLE_CALLOC_ARRAY(block.data, block_size);
- check(block.data != NULL);
- block.len = block_size;
- block_source_from_buf(&block.source, &buf);
- ret = block_writer_init(&bw, BLOCK_TYPE_OBJ, block.data, block_size,
- header_off, hash_size(REFTABLE_HASH_SHA1));
- check(!ret);
-
- for (i = 0; i < N; i++) {
- uint8_t bytes[] = { i, i + 1, i + 2, i + 3, i + 5 }, *allocated;
- DUP_ARRAY(allocated, bytes, ARRAY_SIZE(bytes));
-
- rec.u.obj.hash_prefix = allocated;
- rec.u.obj.hash_prefix_len = 5;
-
- recs[i] = rec;
- ret = block_writer_add(&bw, &rec);
- rec.u.obj.hash_prefix = NULL;
- rec.u.obj.hash_prefix_len = 0;
- check_int(ret, ==, 0);
- }
-
- ret = block_writer_finish(&bw);
- check_int(ret, >, 0);
-
- block_writer_release(&bw);
-
- block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
-
- block_iter_seek_start(&it, &br);
-
- for (i = 0; ; i++) {
- ret = block_iter_next(&it, &rec);
- check_int(ret, >=, 0);
- if (ret > 0) {
- check_int(i, ==, N);
- break;
- }
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- for (i = 0; i < N; i++) {
- block_iter_reset(&it);
- reftable_record_key(&recs[i], &want);
-
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
-
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- block_reader_release(&br);
- block_iter_close(&it);
- reftable_record_release(&rec);
- reftable_block_done(&br.block);
- reftable_buf_release(&want);
- reftable_buf_release(&buf);
- for (i = 0; i < N; i++)
- reftable_record_release(&recs[i]);
-}
-
-static void t_index_block_read_write(void)
-{
- const int header_off = 21;
- struct reftable_record recs[30];
- const size_t N = ARRAY_SIZE(recs);
- const size_t block_size = 1024;
- struct reftable_block block = { 0 };
- struct block_writer bw = {
- .last_key = REFTABLE_BUF_INIT,
- };
- struct reftable_record rec = {
- .type = BLOCK_TYPE_INDEX,
- .u.idx.last_key = REFTABLE_BUF_INIT,
- };
- size_t i = 0;
- int ret;
- struct block_reader br = { 0 };
- struct block_iter it = BLOCK_ITER_INIT;
- struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
-
- REFTABLE_CALLOC_ARRAY(block.data, block_size);
- check(block.data != NULL);
- block.len = block_size;
- block_source_from_buf(&block.source, &buf);
- ret = block_writer_init(&bw, BLOCK_TYPE_INDEX, block.data, block_size,
- header_off, hash_size(REFTABLE_HASH_SHA1));
- check(!ret);
-
- for (i = 0; i < N; i++) {
- char buf[128];
-
- snprintf(buf, sizeof(buf), "branch%02"PRIuMAX, (uintmax_t)i);
-
- reftable_buf_init(&recs[i].u.idx.last_key);
- recs[i].type = BLOCK_TYPE_INDEX;
- check(!reftable_buf_addstr(&recs[i].u.idx.last_key, buf));
- recs[i].u.idx.offset = i;
-
- ret = block_writer_add(&bw, &recs[i]);
- check_int(ret, ==, 0);
- }
-
- ret = block_writer_finish(&bw);
- check_int(ret, >, 0);
-
- block_writer_release(&bw);
-
- block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
-
- block_iter_seek_start(&it, &br);
-
- for (i = 0; ; i++) {
- ret = block_iter_next(&it, &rec);
- check_int(ret, >=, 0);
- if (ret > 0) {
- check_int(i, ==, N);
- break;
- }
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- for (i = 0; i < N; i++) {
- block_iter_reset(&it);
- reftable_record_key(&recs[i], &want);
-
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
-
- check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1));
-
- want.len--;
- ret = block_iter_seek_key(&it, &br, &want);
- check_int(ret, ==, 0);
-
- ret = block_iter_next(&it, &rec);
- check_int(ret, ==, 0);
- check(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1));
- }
-
- block_reader_release(&br);
- block_iter_close(&it);
- reftable_record_release(&rec);
- reftable_block_done(&br.block);
- reftable_buf_release(&want);
- reftable_buf_release(&buf);
- for (i = 0; i < N; i++)
- reftable_record_release(&recs[i]);
-}
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- TEST(t_index_block_read_write(), "read-write operations on index blocks work");
- TEST(t_log_block_read_write(), "read-write operations on log blocks work");
- TEST(t_obj_block_read_write(), "read-write operations on obj blocks work");
- TEST(t_ref_block_read_write(), "read-write operations on ref blocks work");
-
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c
new file mode 100644
index 0000000000..af24901230
--- /dev/null
+++ b/t/unit-tests/u-reftable-block.c
@@ -0,0 +1,373 @@
+/*
+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 "unit-test.h"
+#include "reftable/block.h"
+#include "reftable/blocksource.h"
+#include "reftable/constants.h"
+#include "reftable/reftable-error.h"
+#include "strbuf.h"
+
+void test_reftable_block__index_read_write(void)
+{
+ const int header_off = 21; /* random */
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = REFTABLE_BUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_REF,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ cl_assert(block.data != NULL);
+ block.len = block_size;
+ block_source_from_buf(&block.source ,&buf);
+ ret = block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
+ header_off, hash_size(REFTABLE_HASH_SHA1));
+ cl_assert(ret == 0);
+
+ rec.u.ref.refname = (char *) "";
+ rec.u.ref.value_type = REFTABLE_REF_DELETION;
+ ret = block_writer_add(&bw, &rec);
+ cl_assert_equal_i(ret, REFTABLE_API_ERROR);
+
+ for (i = 0; i < N; i++) {
+ rec.u.ref.refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
+ rec.u.ref.value_type = REFTABLE_REF_VAL1;
+ memset(rec.u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1);
+
+ recs[i] = rec;
+ ret = block_writer_add(&bw, &rec);
+ rec.u.ref.refname = NULL;
+ rec.u.ref.value_type = REFTABLE_REF_DELETION;
+ cl_assert_equal_i(ret, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ cl_assert(ret > 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ cl_assert(ret >= 0);
+ if (ret > 0) {
+ cl_assert_equal_i(i, N);
+ break;
+ }
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_record_key(&recs[i], &want);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+
+ want.len--;
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+ cl_assert_equal_i(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ reftable_buf_release(&want);
+ reftable_buf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+void test_reftable_block__log_read_write(void)
+{
+ const int header_off = 21;
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 2048;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = REFTABLE_BUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_LOG,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ cl_assert(block.data != NULL);
+ block.len = block_size;
+ block_source_from_buf(&block.source ,&buf);
+ ret = block_writer_init(&bw, BLOCK_TYPE_LOG, block.data, block_size,
+ header_off, hash_size(REFTABLE_HASH_SHA1));
+ cl_assert(ret == 0);
+
+ for (i = 0; i < N; i++) {
+ rec.u.log.refname = xstrfmt("branch%02"PRIuMAX , (uintmax_t)i);
+ rec.u.log.update_index = i;
+ rec.u.log.value_type = REFTABLE_LOG_UPDATE;
+
+ recs[i] = rec;
+ ret = block_writer_add(&bw, &rec);
+ rec.u.log.refname = NULL;
+ rec.u.log.value_type = REFTABLE_LOG_DELETION;
+ cl_assert_equal_i(ret, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ cl_assert(ret > 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ cl_assert(ret >= 0);
+ if (ret > 0) {
+ cl_assert_equal_i(i, N);
+ break;
+ }
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_buf_reset(&want);
+ cl_assert(reftable_buf_addstr(&want, recs[i].u.log.refname) == 0);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+
+ want.len--;
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+ cl_assert_equal_i(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ reftable_buf_release(&want);
+ reftable_buf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+void test_reftable_block__obj_read_write(void)
+{
+ const int header_off = 21;
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = REFTABLE_BUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_OBJ,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ cl_assert(block.data != NULL);
+ block.len = block_size;
+ block_source_from_buf(&block.source, &buf);
+ ret = block_writer_init(&bw, BLOCK_TYPE_OBJ, block.data, block_size,
+ header_off, hash_size(REFTABLE_HASH_SHA1));
+ cl_assert(ret == 0);
+
+ for (i = 0; i < N; i++) {
+ uint8_t bytes[] = { i, i + 1, i + 2, i + 3, i + 5 }, *allocated;
+ DUP_ARRAY(allocated, bytes, ARRAY_SIZE(bytes));
+
+ rec.u.obj.hash_prefix = allocated;
+ rec.u.obj.hash_prefix_len = 5;
+
+ recs[i] = rec;
+ ret = block_writer_add(&bw, &rec);
+ rec.u.obj.hash_prefix = NULL;
+ rec.u.obj.hash_prefix_len = 0;
+ cl_assert_equal_i(ret, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ cl_assert(ret > 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ cl_assert(ret >= 0);
+ if (ret > 0) {
+ cl_assert_equal_i(i, N);
+ break;
+ }
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_record_key(&recs[i], &want);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ reftable_buf_release(&want);
+ reftable_buf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
+
+void test_reftable_block__ref_read_write(void)
+{
+ const int header_off = 21;
+ struct reftable_record recs[30];
+ const size_t N = ARRAY_SIZE(recs);
+ const size_t block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
+ .last_key = REFTABLE_BUF_INIT,
+ };
+ struct reftable_record rec = {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.last_key = REFTABLE_BUF_INIT,
+ };
+ size_t i = 0;
+ int ret;
+ struct block_reader br = { 0 };
+ struct block_iter it = BLOCK_ITER_INIT;
+ struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
+
+ REFTABLE_CALLOC_ARRAY(block.data, block_size);
+ cl_assert(block.data != NULL);
+ block.len = block_size;
+ block_source_from_buf(&block.source, &buf);
+ ret = block_writer_init(&bw, BLOCK_TYPE_INDEX, block.data, block_size,
+ header_off, hash_size(REFTABLE_HASH_SHA1));
+ cl_assert(ret == 0);
+
+ for (i = 0; i < N; i++) {
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "branch%02"PRIuMAX, (uintmax_t)i);
+
+ reftable_buf_init(&recs[i].u.idx.last_key);
+ recs[i].type = BLOCK_TYPE_INDEX;
+ cl_assert(reftable_buf_addstr(&recs[i].u.idx.last_key, buf) == 0);
+ recs[i].u.idx.offset = i;
+
+ ret = block_writer_add(&bw, &recs[i]);
+ cl_assert_equal_i(ret, 0);
+ }
+
+ ret = block_writer_finish(&bw);
+ cl_assert(ret > 0);
+
+ block_writer_release(&bw);
+
+ block_reader_init(&br, &block, header_off, block_size, REFTABLE_HASH_SIZE_SHA1);
+
+ block_iter_seek_start(&it, &br);
+
+ for (i = 0; ; i++) {
+ ret = block_iter_next(&it, &rec);
+ cl_assert(ret >= 0);
+ if (ret > 0) {
+ cl_assert_equal_i(i, N);
+ break;
+ }
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ for (i = 0; i < N; i++) {
+ block_iter_reset(&it);
+ reftable_record_key(&recs[i], &want);
+
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+
+ cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+
+ want.len--;
+ ret = block_iter_seek_key(&it, &br, &want);
+ cl_assert_equal_i(ret, 0);
+
+ ret = block_iter_next(&it, &rec);
+ cl_assert_equal_i(ret, 0);
+ cl_assert_equal_i(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1), 1);
+ }
+
+ block_reader_release(&br);
+ block_iter_close(&it);
+ reftable_record_release(&rec);
+ reftable_block_done(&br.block);
+ reftable_buf_release(&want);
+ reftable_buf_release(&buf);
+ for (i = 0; i < N; i++)
+ reftable_record_release(&recs[i]);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar
2025-04-29 17:52 ` [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
2025-05-05 7:37 ` Seyi Chamber
0 siblings, 1 reply; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:52:55PM +0100, Seyi Kuforiji wrote:
> diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
> deleted file mode 100644
> index 22040aeefa..0000000000
> --- a/t/unit-tests/t-reftable-block.c
> +++ /dev/null
Hm, why is this recorded as a delete and creation? Weird, inspecting the
diff locally properly shows it as a rename, which makes it a ton easier
to review. It would be great if you could try to play around with the
`--find-renames` option in the next iteration of this series and double
check that these are shown as a rename.
> diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c
> new file mode 100644
> index 0000000000..af24901230
> --- /dev/null
> +++ b/t/unit-tests/u-reftable-block.c
> @@ -0,0 +1,373 @@
> +/*
> +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 "unit-test.h"
> +#include "reftable/block.h"
> +#include "reftable/blocksource.h"
> +#include "reftable/constants.h"
> +#include "reftable/reftable-error.h"
> +#include "strbuf.h"
> +
> +void test_reftable_block__index_read_write(void)
This doesn't got to do anything with indices but with refs, so I'd
rename this to `__ref_read_write()`.
> +{
> + const int header_off = 21; /* random */
> + struct reftable_record recs[30];
> + const size_t N = ARRAY_SIZE(recs);
> + const size_t block_size = 1024;
> + struct reftable_block block = { 0 };
> + struct block_writer bw = {
> + .last_key = REFTABLE_BUF_INIT,
> + };
> + struct reftable_record rec = {
> + .type = BLOCK_TYPE_REF,
> + };
> + size_t i = 0;
> + int ret;
> + struct block_reader br = { 0 };
> + struct block_iter it = BLOCK_ITER_INIT;
> + struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
> +
> + REFTABLE_CALLOC_ARRAY(block.data, block_size);
> + cl_assert(block.data != NULL);
> + block.len = block_size;
> + block_source_from_buf(&block.source ,&buf);
> + ret = block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
> + header_off, hash_size(REFTABLE_HASH_SHA1));
> + cl_assert(ret == 0);
Same comment here, asserts like this can be retained as
`cl_assert(!ret)`.
> + rec.u.ref.refname = (char *) "";
> + rec.u.ref.value_type = REFTABLE_REF_DELETION;
> + ret = block_writer_add(&bw, &rec);
> + cl_assert_equal_i(ret, REFTABLE_API_ERROR);
> +
> + for (i = 0; i < N; i++) {
> + rec.u.ref.refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
> + rec.u.ref.value_type = REFTABLE_REF_VAL1;
> + memset(rec.u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1);
> +
> + recs[i] = rec;
> + ret = block_writer_add(&bw, &rec);
> + rec.u.ref.refname = NULL;
> + rec.u.ref.value_type = REFTABLE_REF_DELETION;
> + cl_assert_equal_i(ret, 0);
> + }
> +
> + ret = block_writer_finish(&bw);
> + cl_assert(ret > 0);
It's a bit unfortunate that we have to use `cl_assert()` here, but that
isn't the fault of this series. I do have a pull request pending
upstream that introduces integer comparisons. Once we've updated to that
version I'll go through our unit tests and adapt callsites accordingly.
[snip]
> +void test_reftable_block__ref_read_write(void)
This one here should be called `__index_read_write()`. I guess you
confused the first and and this test name with one another.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar
2025-05-02 9:57 ` Patrick Steinhardt
@ 2025-05-05 7:37 ` Seyi Chamber
2025-05-05 9:52 ` Patrick Steinhardt
0 siblings, 1 reply; 31+ messages in thread
From: Seyi Chamber @ 2025-05-05 7:37 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, phillip.wood
On Fri, 2 May 2025 at 10:57, Patrick Steinhardt <ps@pks.im> wrote:
>
> On Tue, Apr 29, 2025 at 06:52:55PM +0100, Seyi Kuforiji wrote:
> > diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
> > deleted file mode 100644
> > index 22040aeefa..0000000000
> > --- a/t/unit-tests/t-reftable-block.c
> > +++ /dev/null
>
> Hm, why is this recorded as a delete and creation? Weird, inspecting the
> diff locally properly shows it as a rename, which makes it a ton easier
> to review. It would be great if you could try to play around with the
> `--find-renames` option in the next iteration of this series and double
> check that these are shown as a rename.
>
I used `--find-renames=90` for this patch series. Is there any
recommended number to set it to? :/
> > diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c
> > new file mode 100644
> > index 0000000000..af24901230
> > --- /dev/null
> > +++ b/t/unit-tests/u-reftable-block.c
> > @@ -0,0 +1,373 @@
> > +/*
> > +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 "unit-test.h"
> > +#include "reftable/block.h"
> > +#include "reftable/blocksource.h"
> > +#include "reftable/constants.h"
> > +#include "reftable/reftable-error.h"
> > +#include "strbuf.h"
> > +
> > +void test_reftable_block__index_read_write(void)
>
> This doesn't got to do anything with indices but with refs, so I'd
> rename this to `__ref_read_write()`.
>
> > +{
> > + const int header_off = 21; /* random */
> > + struct reftable_record recs[30];
> > + const size_t N = ARRAY_SIZE(recs);
> > + const size_t block_size = 1024;
> > + struct reftable_block block = { 0 };
> > + struct block_writer bw = {
> > + .last_key = REFTABLE_BUF_INIT,
> > + };
> > + struct reftable_record rec = {
> > + .type = BLOCK_TYPE_REF,
> > + };
> > + size_t i = 0;
> > + int ret;
> > + struct block_reader br = { 0 };
> > + struct block_iter it = BLOCK_ITER_INIT;
> > + struct reftable_buf want = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
> > +
> > + REFTABLE_CALLOC_ARRAY(block.data, block_size);
> > + cl_assert(block.data != NULL);
> > + block.len = block_size;
> > + block_source_from_buf(&block.source ,&buf);
> > + ret = block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
> > + header_off, hash_size(REFTABLE_HASH_SHA1));
> > + cl_assert(ret == 0);
>
> Same comment here, asserts like this can be retained as
> `cl_assert(!ret)`.
>
ohh, I didn't know this was possible.
> > + rec.u.ref.refname = (char *) "";
> > + rec.u.ref.value_type = REFTABLE_REF_DELETION;
> > + ret = block_writer_add(&bw, &rec);
> > + cl_assert_equal_i(ret, REFTABLE_API_ERROR);
> > +
> > + for (i = 0; i < N; i++) {
> > + rec.u.ref.refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
> > + rec.u.ref.value_type = REFTABLE_REF_VAL1;
> > + memset(rec.u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1);
> > +
> > + recs[i] = rec;
> > + ret = block_writer_add(&bw, &rec);
> > + rec.u.ref.refname = NULL;
> > + rec.u.ref.value_type = REFTABLE_REF_DELETION;
> > + cl_assert_equal_i(ret, 0);
> > + }
> > +
> > + ret = block_writer_finish(&bw);
> > + cl_assert(ret > 0);
>
> It's a bit unfortunate that we have to use `cl_assert()` here, but that
> isn't the fault of this series. I do have a pull request pending
> upstream that introduces integer comparisons. Once we've updated to that
> version I'll go through our unit tests and adapt callsites accordingly.
>
> [snip]
> > +void test_reftable_block__ref_read_write(void)
>
> This one here should be called `__index_read_write()`. I guess you
> confused the first and and this test name with one another.
>
> Patrick
Ah, okay, Thanks!
Best
Seyi
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar
2025-05-05 7:37 ` Seyi Chamber
@ 2025-05-05 9:52 ` Patrick Steinhardt
2025-05-05 21:14 ` Junio C Hamano
0 siblings, 1 reply; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-05 9:52 UTC (permalink / raw)
To: Seyi Chamber; +Cc: git, phillip.wood
On Mon, May 05, 2025 at 08:37:27AM +0100, Seyi Chamber wrote:
> On Fri, 2 May 2025 at 10:57, Patrick Steinhardt <ps@pks.im> wrote:
> >
> > On Tue, Apr 29, 2025 at 06:52:55PM +0100, Seyi Kuforiji wrote:
> > > diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/t-reftable-block.c
> > > deleted file mode 100644
> > > index 22040aeefa..0000000000
> > > --- a/t/unit-tests/t-reftable-block.c
> > > +++ /dev/null
> >
> > Hm, why is this recorded as a delete and creation? Weird, inspecting the
> > diff locally properly shows it as a rename, which makes it a ton easier
> > to review. It would be great if you could try to play around with the
> > `--find-renames` option in the next iteration of this series and double
> > check that these are shown as a rename.
> >
>
> I used `--find-renames=90` for this patch series. Is there any
> recommended number to set it to? :/
Note that "90" also isn't interpreted the way you think it is, at least
going by git-format-patch(1):
-M[<n>], --find-renames[=<n>]
Detect renames. If <n> is specified, it is a threshold on the similarity index (i.e. amount
of addition/deletions compared to the file’s size). For example, -M90% means Git should
consider a delete/add pair to be a rename if more than 90% of the file hasn’t changed.
Without a % sign, the number is to be read as a fraction, with a decimal point before it.
I.e., -M5 becomes 0.5, and is thus the same as -M50%. Similarly, -M05 is the same as -M5%.
To limit detection to exact renames, use -M100%. The default similarity index is 50%.
What you probably wanted to say is `--find-renames=90%`, but without the
percentage sign it is read as a fraction, where 1 means "exact copy" and
0 means "all different".
I'd recommend to just play around with this option and inspect the
outcome until things look reviewable. :)
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar
2025-05-05 9:52 ` Patrick Steinhardt
@ 2025-05-05 21:14 ` Junio C Hamano
2025-05-06 5:10 ` Patrick Steinhardt
0 siblings, 1 reply; 31+ messages in thread
From: Junio C Hamano @ 2025-05-05 21:14 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: Seyi Chamber, git, phillip.wood
Patrick Steinhardt <ps@pks.im> writes:
> -M[<n>], --find-renames[=<n>]
> Detect renames. If <n> is specified, it is a threshold on the similarity index (i.e. amount
> of addition/deletions compared to the file’s size). For example, -M90% means Git should
> consider a delete/add pair to be a rename if more than 90% of the file hasn’t changed.
> Without a % sign, the number is to be read as a fraction, with a decimal point before it.
> I.e., -M5 becomes 0.5, and is thus the same as -M50%. Similarly, -M05 is the same as -M5%.
> To limit detection to exact renames, use -M100%. The default similarity index is 50%.
>
> What you probably wanted to say is `--find-renames=90%`, but without the
> percentage sign it is read as a fraction, where 1 means "exact copy" and
> 0 means "all different".
I am confused. -M<number> without trailing %-sign is taken as
fraction against 1 followed by the same number of '0' has the
<number> has digits. -M5 is 5 over 10, -M50 is 50 over 100. -M90
is 90 over 100, so -M90 and -M90% should mean the same thing.
But you are right. When you want to claim your pre- and post- image
files still correspond with each other in a meaningful way, even
after making extensive change, you would want to _lower_, not raise,
your similarity threshold. If the default is -M50, then -M90 would
be a useful option to reject what Git (mistakenly) thinks are renames
and tell it to instead consider they are removals and creations.
THanks.
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar
2025-05-05 21:14 ` Junio C Hamano
@ 2025-05-06 5:10 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-06 5:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Seyi Chamber, git, phillip.wood
On Mon, May 05, 2025 at 02:14:39PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
>
> > -M[<n>], --find-renames[=<n>]
> > Detect renames. If <n> is specified, it is a threshold on the similarity index (i.e. amount
> > of addition/deletions compared to the file’s size). For example, -M90% means Git should
> > consider a delete/add pair to be a rename if more than 90% of the file hasn’t changed.
> > Without a % sign, the number is to be read as a fraction, with a decimal point before it.
> > I.e., -M5 becomes 0.5, and is thus the same as -M50%. Similarly, -M05 is the same as -M5%.
> > To limit detection to exact renames, use -M100%. The default similarity index is 50%.
> >
> > What you probably wanted to say is `--find-renames=90%`, but without the
> > percentage sign it is read as a fraction, where 1 means "exact copy" and
> > 0 means "all different".
>
> I am confused. -M<number> without trailing %-sign is taken as
> fraction against 1 followed by the same number of '0' has the
> <number> has digits. -M5 is 5 over 10, -M50 is 50 over 100. -M90
> is 90 over 100, so -M90 and -M90% should mean the same thing.
Hm, I guess _I_ was confused then. It doesn't feel natural to me that
-M90 would mean 90% whereas -M5 means 50%, and the documentation isn't
quite clear about this, either.
> But you are right. When you want to claim your pre- and post- image
> files still correspond with each other in a meaningful way, even
> after making extensive change, you would want to _lower_, not raise,
> your similarity threshold. If the default is -M50, then -M90 would
> be a useful option to reject what Git (mistakenly) thinks are renames
> and tell it to instead consider they are removals and creations.
Yeah, at least that point stands :)
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 04/10] t/unit-tests: convert reftable merged test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (2 preceding siblings ...)
2025-04-29 17:52 ` [PATCH v2 03/10] t/unit-tests: convert reftable block test to use clar Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:52 ` [PATCH v2 05/10] t/unit-tests: convert reftable pq " Seyi Kuforiji
` (5 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable merged test file to use clar testing framework by using
clar assertions where necessary.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-merged.c | 546 -------------------------------
t/unit-tests/u-reftable-merged.c | 515 +++++++++++++++++++++++++++++
4 files changed, 517 insertions(+), 548 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-merged.c
create mode 100644 t/unit-tests/u-reftable-merged.c
diff --git a/Makefile b/Makefile
index 239c575dad..2b1642465a 100644
--- a/Makefile
+++ b/Makefile
@@ -1364,6 +1364,7 @@ CLAR_TEST_SUITES += u-oidtree
CLAR_TEST_SUITES += u-prio-queue
CLAR_TEST_SUITES += u-reftable-basics
CLAR_TEST_SUITES += u-reftable-block
+CLAR_TEST_SUITES += u-reftable-merged
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1376,7 +1377,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-merged
UNIT_TEST_PROGRAMS += t-reftable-pq
UNIT_TEST_PROGRAMS += t-reftable-reader
UNIT_TEST_PROGRAMS += t-reftable-readwrite
diff --git a/t/meson.build b/t/meson.build
index e0d723e74b..70a783ba80 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -10,6 +10,7 @@ clar_test_suites = [
'unit-tests/u-prio-queue.c',
'unit-tests/u-reftable-basics.c',
'unit-tests/u-reftable-block.c',
+ 'unit-tests/u-reftable-merged.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -56,7 +57,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-merged.c',
'unit-tests/t-reftable-pq.c',
'unit-tests/t-reftable-reader.c',
'unit-tests/t-reftable-readwrite.c',
diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/t-reftable-merged.c
deleted file mode 100644
index 60836f80d6..0000000000
--- a/t/unit-tests/t-reftable-merged.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
-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 "lib-reftable.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-merged.h"
-#include "reftable/reftable-writer.h"
-
-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 reftable_buf *buf, const size_t n)
-{
- struct reftable_merged_table *mt = NULL;
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- int err;
-
- REFTABLE_CALLOC_ARRAY(*readers, n);
- check(*readers != NULL);
- REFTABLE_CALLOC_ARRAY(*source, n);
- check(*source != NULL);
-
- for (size_t i = 0; i < n; i++) {
- t_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts);
- block_source_from_buf(&(*source)[i], &buf[i]);
-
- err = reftable_reader_new(&(*readers)[i], &(*source)[i],
- "name");
- check(!err);
- }
-
- err = reftable_merged_table_new(&mt, *readers, n, REFTABLE_HASH_SHA1);
- 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_decref(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 reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_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;
-
- err = merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
- check(!err);
- 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, REFTABLE_HASH_SIZE_SHA1));
- 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++)
- reftable_buf_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 reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_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;
-
- err = merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
- check(!err);
- err = reftable_iterator_seek_ref(&it, "a");
- check(!err);
- check_int(reftable_merged_table_hash_id(mt), ==, REFTABLE_HASH_SHA1);
- 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;
-
- check(!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],
- REFTABLE_HASH_SIZE_SHA1));
- for (i = 0; i < len; i++)
- reftable_ref_record_release(&out[i]);
- reftable_free(out);
-
- for (i = 0; i < 3; i++)
- reftable_buf_release(&bufs[i]);
- readers_destroy(readers, 3);
- reftable_merged_table_free(mt);
- reftable_free(bs);
-}
-
-static void t_merged_seek_multiple_times(void)
-{
- struct reftable_ref_record r1[] = {
- {
- .refname = (char *) "a",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1 },
- },
- {
- .refname = (char *) "c",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 2 },
- }
- };
- struct reftable_ref_record r2[] = {
- {
- .refname = (char *) "b",
- .update_index = 2,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 3 },
- },
- {
- .refname = (char *) "d",
- .update_index = 2,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 4 },
- },
- };
- struct reftable_ref_record *refs[] = {
- r1, r2,
- };
- size_t sizes[] = {
- ARRAY_SIZE(r1), ARRAY_SIZE(r2),
- };
- struct reftable_buf bufs[] = {
- REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
- };
- struct reftable_block_source *sources = NULL;
- struct reftable_reader **readers = NULL;
- struct reftable_ref_record rec = { 0 };
- struct reftable_iterator it = { 0 };
- struct reftable_merged_table *mt;
-
- mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2);
- merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
-
- for (size_t i = 0; i < 5; i++) {
- int err = reftable_iterator_seek_ref(&it, "c");
- check(!err);
-
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- err = reftable_ref_record_equal(&rec, &r1[1], REFTABLE_HASH_SIZE_SHA1);
- check(err == 1);
-
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- err = reftable_ref_record_equal(&rec, &r2[1], REFTABLE_HASH_SIZE_SHA1);
- check(err == 1);
-
- err = reftable_iterator_next_ref(&it, &rec);
- check(err > 0);
- }
-
- for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
- reftable_buf_release(&bufs[i]);
- readers_destroy(readers, ARRAY_SIZE(refs));
- reftable_ref_record_release(&rec);
- reftable_iterator_destroy(&it);
- reftable_merged_table_free(mt);
- reftable_free(sources);
-}
-
-static void t_merged_seek_multiple_times_without_draining(void)
-{
- struct reftable_ref_record r1[] = {
- {
- .refname = (char *) "a",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 1 },
- },
- {
- .refname = (char *) "c",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 2 },
- }
- };
- struct reftable_ref_record r2[] = {
- {
- .refname = (char *) "b",
- .update_index = 2,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 3 },
- },
- {
- .refname = (char *) "d",
- .update_index = 2,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 4 },
- },
- };
- struct reftable_ref_record *refs[] = {
- r1, r2,
- };
- size_t sizes[] = {
- ARRAY_SIZE(r1), ARRAY_SIZE(r2),
- };
- struct reftable_buf bufs[] = {
- REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
- };
- struct reftable_block_source *sources = NULL;
- struct reftable_reader **readers = NULL;
- struct reftable_ref_record rec = { 0 };
- struct reftable_iterator it = { 0 };
- struct reftable_merged_table *mt;
- int err;
-
- mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2);
- merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
-
- err = reftable_iterator_seek_ref(&it, "b");
- check(!err);
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- err = reftable_ref_record_equal(&rec, &r2[0], REFTABLE_HASH_SIZE_SHA1);
- check(err == 1);
-
- err = reftable_iterator_seek_ref(&it, "a");
- check(!err);
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- err = reftable_ref_record_equal(&rec, &r1[0], REFTABLE_HASH_SIZE_SHA1);
- check(err == 1);
-
- for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
- reftable_buf_release(&bufs[i]);
- readers_destroy(readers, ARRAY_SIZE(refs));
- reftable_ref_record_release(&rec);
- reftable_iterator_destroy(&it);
- reftable_merged_table_free(mt);
- reftable_free(sources);
-}
-
-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 reftable_buf *buf, const size_t n)
-{
- struct reftable_merged_table *mt = NULL;
- struct reftable_write_options opts = {
- .block_size = 256,
- .exact_log_message = 1,
- };
- int err;
-
- REFTABLE_CALLOC_ARRAY(*readers, n);
- check(*readers != NULL);
- REFTABLE_CALLOC_ARRAY(*source, n);
- check(*source != NULL);
-
- for (size_t i = 0; i < n; i++) {
- t_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts);
- block_source_from_buf(&(*source)[i], &buf[i]);
-
- err = reftable_reader_new(&(*readers)[i], &(*source)[i],
- "name");
- check(!err);
- }
-
- err = reftable_merged_table_new(&mt, *readers, n, REFTABLE_HASH_SHA1);
- 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 reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_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;
-
- err = merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
- check(!err);
- err = reftable_iterator_seek_log(&it, "a");
- check(!err);
- check_int(reftable_merged_table_hash_id(mt), ==, REFTABLE_HASH_SHA1);
- 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;
-
- check(!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],
- REFTABLE_HASH_SIZE_SHA1));
-
- err = merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG);
- check(!err);
- 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], REFTABLE_HASH_SIZE_SHA1));
- 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++)
- reftable_buf_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 reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_ref_record rec = {
- .refname = (char *) "master",
- .update_index = 1,
- };
- int err;
- struct reftable_block_source source = { 0 };
- 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_buf(&source, &buf);
-
- err = reftable_reader_new(&rd, &source, "filename");
- check(!err);
-
- hash_id = reftable_reader_hash_id(rd);
- check_int(hash_id, ==, REFTABLE_HASH_SHA1);
-
- err = reftable_merged_table_new(&merged, &rd, 1, REFTABLE_HASH_SHA256);
- check_int(err, ==, REFTABLE_FORMAT_ERROR);
- err = reftable_merged_table_new(&merged, &rd, 1, REFTABLE_HASH_SHA1);
- check(!err);
-
- reftable_reader_decref(rd);
- reftable_merged_table_free(merged);
- reftable_buf_release(&buf);
-}
-
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- 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_seek_multiple_times(), "merged table can seek multiple times");
- TEST(t_merged_seek_multiple_times_without_draining(), "merged table can seek multiple times without draining");
- TEST(t_merged_single_record(), "ref occurring in only one record can be fetched");
-
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-merged.c b/t/unit-tests/u-reftable-merged.c
new file mode 100644
index 0000000000..48c8f9f6b5
--- /dev/null
+++ b/t/unit-tests/u-reftable-merged.c
@@ -0,0 +1,515 @@
+/*
+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 "unit-test.h"
+#include "lib-reftable.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-merged.h"
+#include "reftable/reftable-writer.h"
+
+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 reftable_buf *buf, const size_t n)
+{
+ struct reftable_merged_table *mt = NULL;
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ };
+ int err;
+
+ REFTABLE_CALLOC_ARRAY(*readers, n);
+ cl_assert(*readers != NULL);
+ REFTABLE_CALLOC_ARRAY(*source, n);
+ cl_assert(*source != NULL);
+
+ for (size_t i = 0; i < n; i++) {
+ cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts);
+ block_source_from_buf(&(*source)[i], &buf[i]);
+
+ err = reftable_reader_new(&(*readers)[i], &(*source)[i],
+ "name");
+ cl_assert(err == 0);
+ }
+
+ err = reftable_merged_table_new(&mt, *readers, n, REFTABLE_HASH_SHA1);
+ cl_assert(err == 0);
+ return mt;
+}
+
+static void readers_destroy(struct reftable_reader **readers, const size_t n)
+{
+ for (size_t i = 0; i < n; i++)
+ reftable_reader_decref(readers[i]);
+ reftable_free(readers);
+}
+
+void test_reftable_merged__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 reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_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;
+
+ err = merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ cl_assert(err == 0);
+ err = reftable_iterator_seek_ref(&it, "a");
+ cl_assert(err == 0);
+
+ err = reftable_iterator_next_ref(&it, &ref);
+ cl_assert(err == 0);
+ cl_assert(reftable_ref_record_equal(&r2[0], &ref, REFTABLE_HASH_SIZE_SHA1) != 0);
+ 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++)
+ reftable_buf_release(&bufs[i]);
+ reftable_free(bs);
+}
+
+void test_reftable_merged__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 reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_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;
+
+ err = merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+ cl_assert(err == 0);
+ err = reftable_iterator_seek_ref(&it, "a");
+ cl_assert(err == 0);
+ cl_assert_equal_i(reftable_merged_table_hash_id(mt), REFTABLE_HASH_SHA1);
+ cl_assert_equal_i(reftable_merged_table_min_update_index(mt), 1);
+ cl_assert_equal_i(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;
+
+ cl_assert(REFTABLE_ALLOC_GROW(out, len + 1, cap) == 0);
+ out[len++] = ref;
+ }
+ reftable_iterator_destroy(&it);
+
+ cl_assert_equal_i(ARRAY_SIZE(want), len);
+ for (i = 0; i < len; i++)
+ cl_assert(reftable_ref_record_equal(want[i], &out[i],
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ for (i = 0; i < len; i++)
+ reftable_ref_record_release(&out[i]);
+ reftable_free(out);
+
+ for (i = 0; i < 3; i++)
+ reftable_buf_release(&bufs[i]);
+ readers_destroy(readers, 3);
+ reftable_merged_table_free(mt);
+ reftable_free(bs);
+}
+
+void test_reftable_merged__merged_seek_multiple_times(void)
+{
+ struct reftable_ref_record r1[] = {
+ {
+ .refname = (char *) "a",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 1 },
+ },
+ {
+ .refname = (char *) "c",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 2 },
+ }
+ };
+ struct reftable_ref_record r2[] = {
+ {
+ .refname = (char *) "b",
+ .update_index = 2,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 3 },
+ },
+ {
+ .refname = (char *) "d",
+ .update_index = 2,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 4 },
+ },
+ };
+ struct reftable_ref_record *refs[] = {
+ r1, r2,
+ };
+ size_t sizes[] = {
+ ARRAY_SIZE(r1), ARRAY_SIZE(r2),
+ };
+ struct reftable_buf bufs[] = {
+ REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
+ };
+ struct reftable_block_source *sources = NULL;
+ struct reftable_reader **readers = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct reftable_merged_table *mt;
+
+ mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2);
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+
+ for (size_t i = 0; i < 5; i++) {
+ int err = reftable_iterator_seek_ref(&it, "c");
+ cl_assert(err == 0);
+
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[1],
+ REFTABLE_HASH_SIZE_SHA1), 1);
+
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[1],
+ REFTABLE_HASH_SIZE_SHA1), 1);
+
+ cl_assert(reftable_iterator_next_ref(&it, &rec) > 0);
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
+ reftable_buf_release(&bufs[i]);
+ readers_destroy(readers, ARRAY_SIZE(refs));
+ reftable_ref_record_release(&rec);
+ reftable_iterator_destroy(&it);
+ reftable_merged_table_free(mt);
+ reftable_free(sources);
+}
+
+void test_reftable_merged__merged_seek_multiple_times_no_drain(void)
+{
+ struct reftable_ref_record r1[] = {
+ {
+ .refname = (char *) "a",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 1 },
+ },
+ {
+ .refname = (char *) "c",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 2 },
+ }
+ };
+ struct reftable_ref_record r2[] = {
+ {
+ .refname = (char *) "b",
+ .update_index = 2,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 3 },
+ },
+ {
+ .refname = (char *) "d",
+ .update_index = 2,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 4 },
+ },
+ };
+ struct reftable_ref_record *refs[] = {
+ r1, r2,
+ };
+ size_t sizes[] = {
+ ARRAY_SIZE(r1), ARRAY_SIZE(r2),
+ };
+ struct reftable_buf bufs[] = {
+ REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
+ };
+ struct reftable_block_source *sources = NULL;
+ struct reftable_reader **readers = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct reftable_merged_table *mt;
+
+ mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2);
+ merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
+
+ cl_assert(reftable_iterator_seek_ref(&it, "b") == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[0],
+ REFTABLE_HASH_SIZE_SHA1), 1);
+
+ cl_assert(reftable_iterator_seek_ref(&it, "a") == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[0],
+ REFTABLE_HASH_SIZE_SHA1), 1);
+
+ for (size_t i = 0; i < ARRAY_SIZE(bufs); i++)
+ reftable_buf_release(&bufs[i]);
+ readers_destroy(readers, ARRAY_SIZE(refs));
+ reftable_ref_record_release(&rec);
+ reftable_iterator_destroy(&it);
+ reftable_merged_table_free(mt);
+ reftable_free(sources);
+}
+
+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 reftable_buf *buf, const size_t n)
+{
+ struct reftable_merged_table *mt = NULL;
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ .exact_log_message = 1,
+ };
+ int err;
+
+ REFTABLE_CALLOC_ARRAY(*readers, n);
+ cl_assert(*readers != NULL);
+ REFTABLE_CALLOC_ARRAY(*source, n);
+ cl_assert(*source != NULL);
+
+ for (size_t i = 0; i < n; i++) {
+ cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts);
+ block_source_from_buf(&(*source)[i], &buf[i]);
+
+ err = reftable_reader_new(&(*readers)[i], &(*source)[i],
+ "name");
+ cl_assert(err == 0);
+ }
+
+ cl_assert(reftable_merged_table_new(&mt, *readers, n, REFTABLE_HASH_SHA1) == 0);
+ return mt;
+}
+
+void test_reftable_merged__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 reftable_buf bufs[3] = { REFTABLE_BUF_INIT, REFTABLE_BUF_INIT, REFTABLE_BUF_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 };
+ struct reftable_log_record *out = NULL;
+ size_t len = 0;
+ size_t cap = 0;
+ size_t i;
+
+ cl_assert(merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG) == 0);
+ cl_assert(reftable_iterator_seek_log(&it, "a") == 0);
+ cl_assert_equal_i(reftable_merged_table_hash_id(mt), REFTABLE_HASH_SHA1);
+ cl_assert_equal_i(reftable_merged_table_min_update_index(mt), 1);
+ cl_assert_equal_i(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;
+
+ cl_assert(REFTABLE_ALLOC_GROW(out, len + 1, cap) == 0);
+ out[len++] = log;
+ }
+ reftable_iterator_destroy(&it);
+
+ cl_assert_equal_i(ARRAY_SIZE(want), len);
+ for (i = 0; i < len; i++)
+ cl_assert(reftable_log_record_equal(want[i], &out[i],
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+
+ cl_assert(merged_table_init_iter(mt, &it, BLOCK_TYPE_LOG) == 0);
+ cl_assert(reftable_iterator_seek_log_at(&it, "a", 2) == 0);
+ reftable_log_record_release(&out[0]);
+ cl_assert(reftable_iterator_next_log(&it, &out[0]) == 0);
+ cl_assert(reftable_log_record_equal(&out[0], &r3[0],
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ 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++)
+ reftable_buf_release(&bufs[i]);
+ readers_destroy(readers, 3);
+ reftable_merged_table_free(mt);
+ reftable_free(bs);
+}
+
+void test_reftable_merged__default_write_opts(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_ref_record rec = {
+ .refname = (char *) "master",
+ .update_index = 1,
+ };
+ int err;
+ struct reftable_block_source source = { 0 };
+ uint32_t hash_id;
+ struct reftable_reader *rd = NULL;
+ struct reftable_merged_table *merged = NULL;
+
+ reftable_writer_set_limits(w, 1, 1);
+
+ cl_assert(reftable_writer_add_ref(w, &rec) == 0);
+
+ cl_assert(reftable_writer_close(w) == 0);
+ reftable_writer_free(w);
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&rd, &source, "filename") == 0);
+
+ hash_id = reftable_reader_hash_id(rd);
+ cl_assert_equal_i(hash_id, REFTABLE_HASH_SHA1);
+
+ err = reftable_merged_table_new(&merged, &rd, 1, REFTABLE_HASH_SHA256);
+ cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR);
+ cl_assert(reftable_merged_table_new(&merged, &rd, 1, REFTABLE_HASH_SHA1) == 0);
+
+ reftable_reader_decref(rd);
+ reftable_merged_table_free(merged);
+ reftable_buf_release(&buf);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 04/10] t/unit-tests: convert reftable merged test to use clar
2025-04-29 17:52 ` [PATCH v2 04/10] t/unit-tests: convert reftable merged " Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:52:56PM +0100, Seyi Kuforiji wrote:
> diff --git a/t/unit-tests/u-reftable-merged.c b/t/unit-tests/u-reftable-merged.c
> new file mode 100644
> index 0000000000..48c8f9f6b5
> --- /dev/null
> +++ b/t/unit-tests/u-reftable-merged.c
> @@ -0,0 +1,515 @@
> +/*
> +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 "unit-test.h"
> +#include "lib-reftable.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-merged.h"
> +#include "reftable/reftable-writer.h"
> +
> +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 reftable_buf *buf, const size_t n)
> +{
> + struct reftable_merged_table *mt = NULL;
> + struct reftable_write_options opts = {
> + .block_size = 256,
> + };
> + int err;
> +
> + REFTABLE_CALLOC_ARRAY(*readers, n);
> + cl_assert(*readers != NULL);
> + REFTABLE_CALLOC_ARRAY(*source, n);
> + cl_assert(*source != NULL);
> +
> + for (size_t i = 0; i < n; i++) {
> + cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts);
> + block_source_from_buf(&(*source)[i], &buf[i]);
> +
> + err = reftable_reader_new(&(*readers)[i], &(*source)[i],
> + "name");
> + cl_assert(err == 0);
> + }
> +
> + err = reftable_merged_table_new(&mt, *readers, n, REFTABLE_HASH_SHA1);
> + cl_assert(err == 0);
> + return mt;
> +}
> +
> +static void readers_destroy(struct reftable_reader **readers, const size_t n)
> +{
> + for (size_t i = 0; i < n; i++)
> + reftable_reader_decref(readers[i]);
> + reftable_free(readers);
> +}
> +
> +void test_reftable_merged__merged_single_record(void)
This should be `__single_record(void)`. Same for the others, the
`__merged` prefix is somewhat duplicate as you already have it in the
test suite name.
> +void test_reftable_merged__merged_seek_multiple_times(void)
So, same here, let's rename to `__seek_multiple_times()`.
> +{
> + struct reftable_ref_record r1[] = {
> + {
> + .refname = (char *) "a",
> + .update_index = 1,
> + .value_type = REFTABLE_REF_VAL1,
> + .value.val1 = { 1 },
> + },
> + {
> + .refname = (char *) "c",
> + .update_index = 1,
> + .value_type = REFTABLE_REF_VAL1,
> + .value.val1 = { 2 },
> + }
> + };
> + struct reftable_ref_record r2[] = {
> + {
> + .refname = (char *) "b",
> + .update_index = 2,
> + .value_type = REFTABLE_REF_VAL1,
> + .value.val1 = { 3 },
> + },
> + {
> + .refname = (char *) "d",
> + .update_index = 2,
> + .value_type = REFTABLE_REF_VAL1,
> + .value.val1 = { 4 },
> + },
> + };
> + struct reftable_ref_record *refs[] = {
> + r1, r2,
> + };
> + size_t sizes[] = {
> + ARRAY_SIZE(r1), ARRAY_SIZE(r2),
> + };
> + struct reftable_buf bufs[] = {
> + REFTABLE_BUF_INIT, REFTABLE_BUF_INIT,
> + };
> + struct reftable_block_source *sources = NULL;
> + struct reftable_reader **readers = NULL;
> + struct reftable_ref_record rec = { 0 };
> + struct reftable_iterator it = { 0 };
> + struct reftable_merged_table *mt;
> +
> + mt = merged_table_from_records(refs, &sources, &readers, sizes, bufs, 2);
> + merged_table_init_iter(mt, &it, BLOCK_TYPE_REF);
> +
> + for (size_t i = 0; i < 5; i++) {
> + int err = reftable_iterator_seek_ref(&it, "c");
> + cl_assert(err == 0);
> +
> + cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
> + cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[1],
> + REFTABLE_HASH_SIZE_SHA1), 1);
Indentation is wrong. Our tabs are 8 spaces, so this is indented too
deep now.
> + cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
> + cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[1],
> + REFTABLE_HASH_SIZE_SHA1), 1);
Same here. There are also other instances in this file.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 05/10] t/unit-tests: convert reftable pq test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (3 preceding siblings ...)
2025-04-29 17:52 ` [PATCH v2 04/10] t/unit-tests: convert reftable merged " Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-04-29 17:52 ` [PATCH v2 06/10] t/unit-tests: convert reftable reader " Seyi Kuforiji
` (4 subsequent siblings)
9 siblings, 0 replies; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable priority queue test file to use clar by using clar
assertions where necessary.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-pq.c | 161 -----------------------------------
t/unit-tests/u-reftable-pq.c | 152 +++++++++++++++++++++++++++++++++
4 files changed, 154 insertions(+), 163 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-pq.c
create mode 100644 t/unit-tests/u-reftable-pq.c
diff --git a/Makefile b/Makefile
index 2b1642465a..4142927d0a 100644
--- a/Makefile
+++ b/Makefile
@@ -1365,6 +1365,7 @@ CLAR_TEST_SUITES += u-prio-queue
CLAR_TEST_SUITES += u-reftable-basics
CLAR_TEST_SUITES += u-reftable-block
CLAR_TEST_SUITES += u-reftable-merged
+CLAR_TEST_SUITES += u-reftable-pq
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1377,7 +1378,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-pq
UNIT_TEST_PROGRAMS += t-reftable-reader
UNIT_TEST_PROGRAMS += t-reftable-readwrite
UNIT_TEST_PROGRAMS += t-reftable-record
diff --git a/t/meson.build b/t/meson.build
index 70a783ba80..9bded2d15c 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -11,6 +11,7 @@ clar_test_suites = [
'unit-tests/u-reftable-basics.c',
'unit-tests/u-reftable-block.c',
'unit-tests/u-reftable-merged.c',
+ 'unit-tests/u-reftable-pq.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -57,7 +58,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-pq.c',
'unit-tests/t-reftable-reader.c',
'unit-tests/t-reftable-readwrite.c',
'unit-tests/t-reftable-record.c',
diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c
deleted file mode 100644
index c128fe8616..0000000000
--- a/t/unit-tests/t-reftable-pq.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
-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"
-#include "strbuf.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)
-{
- int cmp;
- check(!reftable_record_cmp(a->rec, b->rec, &cmp));
- return !cmp && (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++) {
- check(!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;
-
- check(!merged_iter_pqueue_remove(&pq, &e));
- 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++) {
- check(!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;
-
- check(!merged_iter_pqueue_remove(&pq, &e));
- 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++) {
- check(!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;
-
- check(!merged_iter_pqueue_remove(&pq, &e));
-
- merged_iter_pqueue_check(&pq);
- check(pq_entry_equal(&top, &e));
- check(reftable_record_equal(top.rec, &recs[i], REFTABLE_HASH_SIZE_SHA1));
- 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 UNUSED, const char *argv[] UNUSED)
-{
- 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/u-reftable-pq.c b/t/unit-tests/u-reftable-pq.c
new file mode 100644
index 0000000000..ecbf08586c
--- /dev/null
+++ b/t/unit-tests/u-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 "unit-test.h"
+#include "reftable/constants.h"
+#include "reftable/pq.h"
+#include "strbuf.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;
+ cl_assert(pq_less(&pq->heap[parent], &pq->heap[i]) != 0);
+ }
+}
+
+static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b)
+{
+ int cmp;
+ cl_assert(reftable_record_cmp(a->rec, b->rec, &cmp) == 0);
+ return !cmp && (a->index == b->index);
+}
+
+void test_reftable_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++) {
+ cl_assert(reftable_record_init(&recs[i], BLOCK_TYPE_REF) == 0);
+ 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;
+
+ cl_assert(merged_iter_pqueue_remove(&pq, &e) == 0);
+ merged_iter_pqueue_check(&pq);
+
+ cl_assert(pq_entry_equal(&top, &e) != 0);
+ cl_assert(reftable_record_type(e.rec) == BLOCK_TYPE_REF);
+ if (last)
+ cl_assert(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);
+}
+
+void test_reftable_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++) {
+ cl_assert(reftable_record_init(&recs[i], BLOCK_TYPE_REF) == 0);
+ 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;
+
+ cl_assert(merged_iter_pqueue_remove(&pq, &e) == 0);
+ merged_iter_pqueue_check(&pq);
+
+ cl_assert(pq_entry_equal(&top, &e) != 0);
+ cl_assert(reftable_record_type(e.rec) == BLOCK_TYPE_REF);
+ cl_assert_equal_i(e.index, i);
+ if (last)
+ cl_assert_equal_s(last, e.rec->u.ref.refname);
+ last = e.rec->u.ref.refname;
+ }
+
+ merged_iter_pqueue_release(&pq);
+}
+
+void test_reftable_pq__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++) {
+ cl_assert(reftable_record_init(&recs[i], BLOCK_TYPE_REF) == 0);
+ 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;
+
+ cl_assert(merged_iter_pqueue_remove(&pq, &e) == 0);
+
+ merged_iter_pqueue_check(&pq);
+ cl_assert(pq_entry_equal(&top, &e) != 0);
+ cl_assert(reftable_record_equal(top.rec, &recs[i], REFTABLE_HASH_SIZE_SHA1) != 0);
+ for (size_t j = 0; i < pq.len; j++) {
+ cl_assert(pq_less(&top, &pq.heap[j]) != 0);
+ cl_assert(top.index > j);
+ }
+ }
+
+ merged_iter_pqueue_release(&pq);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v2 06/10] t/unit-tests: convert reftable reader test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (4 preceding siblings ...)
2025-04-29 17:52 ` [PATCH v2 05/10] t/unit-tests: convert reftable pq " Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:52 ` [PATCH v2 07/10] t/unit-tests: convert reftable readwrite " Seyi Kuforiji
` (3 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable reader test file to use clar by using clar assertions
where necessary.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-reader.c | 96 --------------------------------
t/unit-tests/u-reftable-reader.c | 78 ++++++++++++++++++++++++++
4 files changed, 80 insertions(+), 98 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-reader.c
create mode 100644 t/unit-tests/u-reftable-reader.c
diff --git a/Makefile b/Makefile
index 4142927d0a..d3e8677653 100644
--- a/Makefile
+++ b/Makefile
@@ -1366,6 +1366,7 @@ CLAR_TEST_SUITES += u-reftable-basics
CLAR_TEST_SUITES += u-reftable-block
CLAR_TEST_SUITES += u-reftable-merged
CLAR_TEST_SUITES += u-reftable-pq
+CLAR_TEST_SUITES += u-reftable-reader
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1378,7 +1379,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-reader
UNIT_TEST_PROGRAMS += t-reftable-readwrite
UNIT_TEST_PROGRAMS += t-reftable-record
UNIT_TEST_PROGRAMS += t-reftable-stack
diff --git a/t/meson.build b/t/meson.build
index 9bded2d15c..6a22bd2790 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -12,6 +12,7 @@ clar_test_suites = [
'unit-tests/u-reftable-block.c',
'unit-tests/u-reftable-merged.c',
'unit-tests/u-reftable-pq.c',
+ 'unit-tests/u-reftable-reader.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -58,7 +59,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-reader.c',
'unit-tests/t-reftable-readwrite.c',
'unit-tests/t-reftable-record.c',
'unit-tests/t-reftable-stack.c',
diff --git a/t/unit-tests/t-reftable-reader.c b/t/unit-tests/t-reftable-reader.c
deleted file mode 100644
index 546df6005e..0000000000
--- a/t/unit-tests/t-reftable-reader.c
+++ /dev/null
@@ -1,96 +0,0 @@
-#include "test-lib.h"
-#include "lib-reftable.h"
-#include "reftable/blocksource.h"
-#include "reftable/reader.h"
-
-static int t_reader_seek_once(void)
-{
- struct reftable_ref_record records[] = {
- {
- .refname = (char *) "refs/heads/main",
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 42 },
- },
- };
- struct reftable_block_source source = { 0 };
- struct reftable_ref_record ref = { 0 };
- struct reftable_iterator it = { 0 };
- struct reftable_reader *reader;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- int ret;
-
- t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
- block_source_from_buf(&source, &buf);
-
- ret = reftable_reader_new(&reader, &source, "name");
- check(!ret);
-
- reftable_reader_init_ref_iterator(reader, &it);
- ret = reftable_iterator_seek_ref(&it, "");
- check(!ret);
- ret = reftable_iterator_next_ref(&it, &ref);
- check(!ret);
-
- ret = reftable_ref_record_equal(&ref, &records[0], REFTABLE_HASH_SIZE_SHA1);
- check_int(ret, ==, 1);
-
- ret = reftable_iterator_next_ref(&it, &ref);
- check_int(ret, ==, 1);
-
- reftable_ref_record_release(&ref);
- reftable_iterator_destroy(&it);
- reftable_reader_decref(reader);
- reftable_buf_release(&buf);
- return 0;
-}
-
-static int t_reader_reseek(void)
-{
- struct reftable_ref_record records[] = {
- {
- .refname = (char *) "refs/heads/main",
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { 42 },
- },
- };
- struct reftable_block_source source = { 0 };
- struct reftable_ref_record ref = { 0 };
- struct reftable_iterator it = { 0 };
- struct reftable_reader *reader;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- int ret;
-
- t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
- block_source_from_buf(&source, &buf);
-
- ret = reftable_reader_new(&reader, &source, "name");
- check(!ret);
-
- reftable_reader_init_ref_iterator(reader, &it);
-
- for (size_t i = 0; i < 5; i++) {
- ret = reftable_iterator_seek_ref(&it, "");
- check(!ret);
- ret = reftable_iterator_next_ref(&it, &ref);
- check(!ret);
-
- ret = reftable_ref_record_equal(&ref, &records[0], REFTABLE_HASH_SIZE_SHA1);
- check_int(ret, ==, 1);
-
- ret = reftable_iterator_next_ref(&it, &ref);
- check_int(ret, ==, 1);
- }
-
- reftable_ref_record_release(&ref);
- reftable_iterator_destroy(&it);
- reftable_reader_decref(reader);
- reftable_buf_release(&buf);
- return 0;
-}
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- TEST(t_reader_seek_once(), "reader can seek once");
- TEST(t_reader_reseek(), "reader can reseek multiple times");
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-reader.c b/t/unit-tests/u-reftable-reader.c
new file mode 100644
index 0000000000..6c35063105
--- /dev/null
+++ b/t/unit-tests/u-reftable-reader.c
@@ -0,0 +1,78 @@
+#include "unit-test.h"
+#include "lib-reftable.h"
+#include "reftable/blocksource.h"
+#include "reftable/reader.h"
+
+void test_reftable_reader__seek_once(void)
+{
+ struct reftable_ref_record records[] = {
+ {
+ .refname = (char *) "refs/heads/main",
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 42 },
+ },
+ };
+ struct reftable_block_source source = { 0 };
+ struct reftable_ref_record ref = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct reftable_reader *reader;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+
+ cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "name") == 0);
+
+
+ reftable_reader_init_ref_iterator(reader, &it);
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &ref) == 0);
+
+ cl_assert_equal_i(reftable_ref_record_equal(&ref, &records[0],
+ REFTABLE_HASH_SIZE_SHA1), 1);
+
+ cl_assert_equal_i(reftable_iterator_next_ref(&it, &ref), 1);
+
+ reftable_ref_record_release(&ref);
+ reftable_iterator_destroy(&it);
+ reftable_reader_decref(reader);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_reader__reseek(void)
+{
+ struct reftable_ref_record records[] = {
+ {
+ .refname = (char *) "refs/heads/main",
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { 42 },
+ },
+ };
+ struct reftable_block_source source = { 0 };
+ struct reftable_ref_record ref = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct reftable_reader *reader;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+
+ cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "name") == 0);
+
+ reftable_reader_init_ref_iterator(reader, &it);
+
+ for (size_t i = 0; i < 5; i++) {
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &ref) == 0);
+
+ cl_assert_equal_i(reftable_ref_record_equal(&ref, &records[0],
+ REFTABLE_HASH_SIZE_SHA1), 1);
+
+ cl_assert_equal_i(reftable_iterator_next_ref(&it, &ref), 1);
+ }
+
+ reftable_ref_record_release(&ref);
+ reftable_iterator_destroy(&it);
+ reftable_reader_decref(reader);
+ reftable_buf_release(&buf);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 06/10] t/unit-tests: convert reftable reader test to use clar
2025-04-29 17:52 ` [PATCH v2 06/10] t/unit-tests: convert reftable reader " Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:52:58PM +0100, Seyi Kuforiji wrote:
> diff --git a/t/unit-tests/u-reftable-reader.c b/t/unit-tests/u-reftable-reader.c
> new file mode 100644
> index 0000000000..6c35063105
> --- /dev/null
> +++ b/t/unit-tests/u-reftable-reader.c
> @@ -0,0 +1,78 @@
> +#include "unit-test.h"
> +#include "lib-reftable.h"
> +#include "reftable/blocksource.h"
> +#include "reftable/reader.h"
> +
> +void test_reftable_reader__seek_once(void)
> +{
> + struct reftable_ref_record records[] = {
> + {
> + .refname = (char *) "refs/heads/main",
> + .value_type = REFTABLE_REF_VAL1,
> + .value.val1 = { 42 },
> + },
> + };
> + struct reftable_block_source source = { 0 };
> + struct reftable_ref_record ref = { 0 };
> + struct reftable_iterator it = { 0 };
> + struct reftable_reader *reader;
> + struct reftable_buf buf = REFTABLE_BUF_INIT;
> +
> + cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
> + block_source_from_buf(&source, &buf);
> +
> + cl_assert(reftable_reader_new(&reader, &source, "name") == 0);
> +
> +
Nit: empty newline.
> + reftable_reader_init_ref_iterator(reader, &it);
> + cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
> + cl_assert(reftable_iterator_next_ref(&it, &ref) == 0);
> +
> + cl_assert_equal_i(reftable_ref_record_equal(&ref, &records[0],
> + REFTABLE_HASH_SIZE_SHA1), 1);
> +
Indentation is wrong again.
> + cl_assert_equal_i(reftable_iterator_next_ref(&it, &ref), 1);
> +
> + reftable_ref_record_release(&ref);
> + reftable_iterator_destroy(&it);
> + reftable_reader_decref(reader);
> + reftable_buf_release(&buf);
> +}
> +
> +void test_reftable_reader__reseek(void)
> +{
> + struct reftable_ref_record records[] = {
> + {
> + .refname = (char *) "refs/heads/main",
> + .value_type = REFTABLE_REF_VAL1,
> + .value.val1 = { 42 },
> + },
> + };
> + struct reftable_block_source source = { 0 };
> + struct reftable_ref_record ref = { 0 };
> + struct reftable_iterator it = { 0 };
> + struct reftable_reader *reader;
> + struct reftable_buf buf = REFTABLE_BUF_INIT;
> +
> + cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL);
> + block_source_from_buf(&source, &buf);
> +
> + cl_assert(reftable_reader_new(&reader, &source, "name") == 0);
> +
> + reftable_reader_init_ref_iterator(reader, &it);
> +
> + for (size_t i = 0; i < 5; i++) {
> + cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
> + cl_assert(reftable_iterator_next_ref(&it, &ref) == 0);
> +
> + cl_assert_equal_i(reftable_ref_record_equal(&ref, &records[0],
> + REFTABLE_HASH_SIZE_SHA1), 1);
Here, as well.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 07/10] t/unit-tests: convert reftable readwrite test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (5 preceding siblings ...)
2025-04-29 17:52 ` [PATCH v2 06/10] t/unit-tests: convert reftable reader " Seyi Kuforiji
@ 2025-04-29 17:52 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:53 ` [PATCH v2 08/10] t/unit-tests: convert reftable record " Seyi Kuforiji
` (2 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:52 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable readwrite test file to use clar by using clar assertions
where necessary.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-readwrite.c | 985 ----------------------------
t/unit-tests/u-reftable-readwrite.c | 870 ++++++++++++++++++++++++
4 files changed, 872 insertions(+), 987 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-readwrite.c
create mode 100644 t/unit-tests/u-reftable-readwrite.c
diff --git a/Makefile b/Makefile
index d3e8677653..88b6851b37 100644
--- a/Makefile
+++ b/Makefile
@@ -1367,6 +1367,7 @@ CLAR_TEST_SUITES += u-reftable-block
CLAR_TEST_SUITES += u-reftable-merged
CLAR_TEST_SUITES += u-reftable-pq
CLAR_TEST_SUITES += u-reftable-reader
+CLAR_TEST_SUITES += u-reftable-readwrite
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1379,7 +1380,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-readwrite
UNIT_TEST_PROGRAMS += t-reftable-record
UNIT_TEST_PROGRAMS += t-reftable-stack
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
diff --git a/t/meson.build b/t/meson.build
index 6a22bd2790..7722d177e2 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -13,6 +13,7 @@ clar_test_suites = [
'unit-tests/u-reftable-merged.c',
'unit-tests/u-reftable-pq.c',
'unit-tests/u-reftable-reader.c',
+ 'unit-tests/u-reftable-readwrite.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -59,7 +60,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-readwrite.c',
'unit-tests/t-reftable-record.c',
'unit-tests/t-reftable-stack.c',
]
diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/t-reftable-readwrite.c
deleted file mode 100644
index c9626831da..0000000000
--- a/t/unit-tests/t-reftable-readwrite.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
-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
-*/
-
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
-#include "test-lib.h"
-#include "lib-reftable.h"
-#include "reftable/basics.h"
-#include "reftable/blocksource.h"
-#include "reftable/reader.h"
-#include "reftable/reftable-error.h"
-#include "reftable/reftable-writer.h"
-#include "strbuf.h"
-
-static const int update_index = 5;
-
-static void t_buffer(void)
-{
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_block out = { 0 };
- int n;
- uint8_t in[] = "hello";
- check(!reftable_buf_add(&buf, in, sizeof(in)));
- block_source_from_buf(&source, &buf);
- check_int(block_source_size(&source), ==, 6);
- n = block_source_read_block(&source, &out, 0, sizeof(in));
- check_int(n, ==, sizeof(in));
- check(!memcmp(in, out.data, n));
- reftable_block_done(&out);
-
- n = block_source_read_block(&source, &out, 1, 2);
- check_int(n, ==, 2);
- check(!memcmp(out.data, "el", 2));
-
- reftable_block_done(&out);
- block_source_close(&source);
- reftable_buf_release(&buf);
-}
-
-static void write_table(char ***names, struct reftable_buf *buf, int N,
- int block_size, enum reftable_hash hash_id)
-{
- struct reftable_write_options opts = {
- .block_size = block_size,
- .hash_id = hash_id,
- };
- struct reftable_ref_record *refs;
- struct reftable_log_record *logs;
- int i;
-
- REFTABLE_CALLOC_ARRAY(*names, N + 1);
- check(*names != NULL);
- REFTABLE_CALLOC_ARRAY(refs, N);
- check(refs != NULL);
- REFTABLE_CALLOC_ARRAY(logs, N);
- check(logs != NULL);
-
- for (i = 0; i < N; i++) {
- refs[i].refname = (*names)[i] = xstrfmt("refs/heads/branch%02d", i);
- refs[i].update_index = update_index;
- refs[i].value_type = REFTABLE_REF_VAL1;
- t_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1);
- }
-
- for (i = 0; i < N; i++) {
- logs[i].refname = (*names)[i];
- logs[i].update_index = update_index;
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- t_reftable_set_hash(logs[i].value.update.new_hash, i,
- REFTABLE_HASH_SHA1);
- logs[i].value.update.message = (char *) "message";
- }
-
- t_reftable_write_to_buf(buf, refs, N, logs, N, &opts);
-
- reftable_free(refs);
- reftable_free(logs);
-}
-
-static void t_log_buffer_size(void)
-{
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_write_options opts = {
- .block_size = 4096,
- };
- int err;
- int i;
- struct reftable_log_record
- log = { .refname = (char *) "refs/heads/master",
- .update_index = update_index,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = { .update = {
- .name = (char *) "Han-Wen Nienhuys",
- .email = (char *) "hanwen@google.com",
- .tz_offset = 100,
- .time = 0x5e430672,
- .message = (char *) "commit: 9\n",
- } } };
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
-
- /* This tests buffer extension for log compression. Must use a random
- hash, to ensure that the compressed part is larger than the original.
- */
- for (i = 0; i < REFTABLE_HASH_SIZE_SHA1; i++) {
- log.value.update.old_hash[i] = (uint8_t)(git_rand(0) % 256);
- log.value.update.new_hash[i] = (uint8_t)(git_rand(0) % 256);
- }
- reftable_writer_set_limits(w, update_index, update_index);
- err = reftable_writer_add_log(w, &log);
- check(!err);
- err = reftable_writer_close(w);
- check(!err);
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_log_overflow(void)
-{
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- char msg[256] = { 0 };
- struct reftable_write_options opts = {
- .block_size = ARRAY_SIZE(msg),
- };
- int err;
- struct reftable_log_record log = {
- .refname = (char *) "refs/heads/master",
- .update_index = update_index,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .old_hash = { 1 },
- .new_hash = { 2 },
- .name = (char *) "Han-Wen Nienhuys",
- .email = (char *) "hanwen@google.com",
- .tz_offset = 100,
- .time = 0x5e430672,
- .message = msg,
- },
- },
- };
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
-
- memset(msg, 'x', sizeof(msg) - 1);
- reftable_writer_set_limits(w, update_index, update_index);
- err = reftable_writer_add_log(w, &log);
- check_int(err, ==, REFTABLE_ENTRY_TOO_BIG_ERROR);
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_log_write_limits(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_log_record log = {
- .refname = (char *)"refs/head/master",
- .update_index = 0,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .old_hash = { 1 },
- .new_hash = { 2 },
- .name = (char *)"Han-Wen Nienhuys",
- .email = (char *)"hanwen@google.com",
- .tz_offset = 100,
- .time = 0x5e430672,
- },
- },
- };
- int err;
-
- reftable_writer_set_limits(w, 1, 1);
-
- /* write with update_index (0) below set limits (1, 1) */
- err = reftable_writer_add_log(w, &log);
- check_int(err, ==, 0);
-
- /* write with update_index (1) in the set limits (1, 1) */
- log.update_index = 1;
- err = reftable_writer_add_log(w, &log);
- check_int(err, ==, 0);
-
- /* write with update_index (3) above set limits (1, 1) */
- log.update_index = 3;
- err = reftable_writer_add_log(w, &log);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_log_write_read(void)
-{
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_ref_record ref = { 0 };
- struct reftable_log_record log = { 0 };
- struct reftable_iterator it = { 0 };
- struct reftable_reader *reader;
- struct reftable_block_source source = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- const struct reftable_stats *stats = NULL;
- int N = 2, err, i, n;
- char **names;
-
- names = reftable_calloc(N + 1, sizeof(*names));
- check(names != NULL);
-
- reftable_writer_set_limits(w, 0, N);
-
- for (i = 0; i < N; i++) {
- char name[256];
- struct reftable_ref_record ref = { 0 };
- snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7);
- names[i] = xstrdup(name);
- ref.refname = name;
- ref.update_index = i;
-
- err = reftable_writer_add_ref(w, &ref);
- check(!err);
- }
-
- for (i = 0; i < N; i++) {
- struct reftable_log_record log = { 0 };
-
- log.refname = names[i];
- log.update_index = i;
- log.value_type = REFTABLE_LOG_UPDATE;
- t_reftable_set_hash(log.value.update.old_hash, i,
- REFTABLE_HASH_SHA1);
- t_reftable_set_hash(log.value.update.new_hash, i + 1,
- REFTABLE_HASH_SHA1);
-
- err = reftable_writer_add_log(w, &log);
- check(!err);
- }
-
- n = reftable_writer_close(w);
- check_int(n, ==, 0);
-
- stats = reftable_writer_stats(w);
- check_int(stats->log_stats.blocks, >, 0);
- reftable_writer_free(w);
- w = NULL;
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&reader, &source, "file.log");
- check(!err);
-
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
-
- err = reftable_iterator_seek_ref(&it, names[N - 1]);
- check(!err);
-
- err = reftable_iterator_next_ref(&it, &ref);
- check(!err);
-
- /* end of iteration. */
- err = reftable_iterator_next_ref(&it, &ref);
- check_int(err, >, 0);
-
- reftable_iterator_destroy(&it);
- reftable_ref_record_release(&ref);
-
- err = reftable_reader_init_log_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_log(&it, "");
- check(!err);
-
- for (i = 0; ; i++) {
- int err = reftable_iterator_next_log(&it, &log);
- if (err > 0)
- break;
- check(!err);
- check_str(names[i], log.refname);
- check_int(i, ==, log.update_index);
- reftable_log_record_release(&log);
- }
-
- check_int(i, ==, N);
- reftable_iterator_destroy(&it);
-
- /* cleanup. */
- reftable_buf_release(&buf);
- free_names(names);
- reftable_reader_decref(reader);
-}
-
-static void t_log_zlib_corruption(void)
-{
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_iterator it = { 0 };
- struct reftable_reader *reader;
- struct reftable_block_source source = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- const struct reftable_stats *stats = NULL;
- char message[100] = { 0 };
- int err, i, n;
- struct reftable_log_record log = {
- .refname = (char *) "refname",
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .new_hash = { 1 },
- .old_hash = { 2 },
- .name = (char *) "My Name",
- .email = (char *) "myname@invalid",
- .message = message,
- },
- },
- };
-
- for (i = 0; i < sizeof(message) - 1; i++)
- message[i] = (uint8_t)(git_rand(0) % 64 + ' ');
-
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_add_log(w, &log);
- check(!err);
-
- n = reftable_writer_close(w);
- check_int(n, ==, 0);
-
- stats = reftable_writer_stats(w);
- check_int(stats->log_stats.blocks, >, 0);
- reftable_writer_free(w);
- w = NULL;
-
- /* corrupt the data. */
- buf.buf[50] ^= 0x99;
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&reader, &source, "file.log");
- check(!err);
-
- err = reftable_reader_init_log_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_log(&it, "refname");
- check_int(err, ==, REFTABLE_ZLIB_ERROR);
-
- reftable_iterator_destroy(&it);
-
- /* cleanup. */
- reftable_reader_decref(reader);
- reftable_buf_release(&buf);
-}
-
-static void t_table_read_write_sequential(void)
-{
- char **names;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- int N = 50;
- struct reftable_iterator it = { 0 };
- struct reftable_block_source source = { 0 };
- struct reftable_reader *reader;
- int err = 0;
- int j = 0;
-
- write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1);
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&reader, &source, "file.ref");
- check(!err);
-
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, "");
- check(!err);
-
- for (j = 0; ; j++) {
- struct reftable_ref_record ref = { 0 };
- int r = reftable_iterator_next_ref(&it, &ref);
- check_int(r, >=, 0);
- if (r > 0)
- break;
- check_str(names[j], ref.refname);
- check_int(update_index, ==, ref.update_index);
- reftable_ref_record_release(&ref);
- }
- check_int(j, ==, N);
-
- reftable_iterator_destroy(&it);
- reftable_reader_decref(reader);
- reftable_buf_release(&buf);
- free_names(names);
-}
-
-static void t_table_write_small_table(void)
-{
- char **names;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- int N = 1;
- write_table(&names, &buf, N, 4096, REFTABLE_HASH_SHA1);
- check_int(buf.len, <, 200);
- reftable_buf_release(&buf);
- free_names(names);
-}
-
-static void t_table_read_api(void)
-{
- char **names;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- int N = 50;
- struct reftable_reader *reader;
- struct reftable_block_source source = { 0 };
- int err;
- struct reftable_log_record log = { 0 };
- struct reftable_iterator it = { 0 };
-
- write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1);
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&reader, &source, "file.ref");
- check(!err);
-
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, names[0]);
- check(!err);
-
- err = reftable_iterator_next_log(&it, &log);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- reftable_buf_release(&buf);
- free_names(names);
- reftable_iterator_destroy(&it);
- reftable_reader_decref(reader);
- reftable_buf_release(&buf);
-}
-
-static void t_table_read_write_seek(int index, enum reftable_hash hash_id)
-{
- char **names;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- int N = 50;
- struct reftable_reader *reader;
- struct reftable_block_source source = { 0 };
- int err;
- int i = 0;
-
- struct reftable_iterator it = { 0 };
- struct reftable_buf pastLast = REFTABLE_BUF_INIT;
- struct reftable_ref_record ref = { 0 };
-
- write_table(&names, &buf, N, 256, hash_id);
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&reader, &source, "file.ref");
- check(!err);
- check_int(hash_id, ==, reftable_reader_hash_id(reader));
-
- if (!index) {
- reader->ref_offsets.index_offset = 0;
- } else {
- check_int(reader->ref_offsets.index_offset, >, 0);
- }
-
- for (i = 1; i < N; i++) {
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, names[i]);
- check(!err);
- err = reftable_iterator_next_ref(&it, &ref);
- check(!err);
- check_str(names[i], ref.refname);
- check_int(REFTABLE_REF_VAL1, ==, ref.value_type);
- check_int(i, ==, ref.value.val1[0]);
-
- reftable_ref_record_release(&ref);
- reftable_iterator_destroy(&it);
- }
-
- check(!reftable_buf_addstr(&pastLast, names[N - 1]));
- check(!reftable_buf_addstr(&pastLast, "/"));
-
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, pastLast.buf);
- if (err == 0) {
- struct reftable_ref_record ref = { 0 };
- int err = reftable_iterator_next_ref(&it, &ref);
- check_int(err, >, 0);
- } else {
- check_int(err, >, 0);
- }
-
- reftable_buf_release(&pastLast);
- reftable_iterator_destroy(&it);
-
- reftable_buf_release(&buf);
- free_names(names);
- reftable_reader_decref(reader);
-}
-
-static void t_table_read_write_seek_linear(void)
-{
- t_table_read_write_seek(0, REFTABLE_HASH_SHA1);
-}
-
-static void t_table_read_write_seek_linear_sha256(void)
-{
- t_table_read_write_seek(0, REFTABLE_HASH_SHA256);
-}
-
-static void t_table_read_write_seek_index(void)
-{
- t_table_read_write_seek(1, REFTABLE_HASH_SHA1);
-}
-
-static void t_table_refs_for(int indexed)
-{
- char **want_names;
- int want_names_len = 0;
- uint8_t want_hash[REFTABLE_HASH_SIZE_SHA1];
-
- struct reftable_write_options opts = {
- .block_size = 256,
- };
- struct reftable_ref_record ref = { 0 };
- struct reftable_reader *reader;
- struct reftable_block_source source = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_iterator it = { 0 };
- int N = 50, n, j, err, i;
-
- want_names = reftable_calloc(N + 1, sizeof(*want_names));
- check(want_names != NULL);
-
- t_reftable_set_hash(want_hash, 4, REFTABLE_HASH_SHA1);
-
- for (i = 0; i < N; i++) {
- uint8_t hash[REFTABLE_HASH_SIZE_SHA1];
- char fill[51] = { 0 };
- char name[100];
- struct reftable_ref_record ref = { 0 };
-
- memset(hash, i, sizeof(hash));
- memset(fill, 'x', 50);
- /* Put the variable part in the start */
- snprintf(name, sizeof(name), "br%02d%s", i, fill);
- name[40] = 0;
- ref.refname = name;
-
- ref.value_type = REFTABLE_REF_VAL2;
- t_reftable_set_hash(ref.value.val2.value, i / 4,
- REFTABLE_HASH_SHA1);
- t_reftable_set_hash(ref.value.val2.target_value, 3 + i / 4,
- REFTABLE_HASH_SHA1);
-
- /* 80 bytes / entry, so 3 entries per block. Yields 17
- */
- /* blocks. */
- n = reftable_writer_add_ref(w, &ref);
- check_int(n, ==, 0);
-
- if (!memcmp(ref.value.val2.value, want_hash, REFTABLE_HASH_SIZE_SHA1) ||
- !memcmp(ref.value.val2.target_value, want_hash, REFTABLE_HASH_SIZE_SHA1))
- want_names[want_names_len++] = xstrdup(name);
- }
-
- n = reftable_writer_close(w);
- check_int(n, ==, 0);
-
- reftable_writer_free(w);
- w = NULL;
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&reader, &source, "file.ref");
- check(!err);
- if (!indexed)
- reader->obj_offsets.is_present = 0;
-
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, "");
- check(!err);
- reftable_iterator_destroy(&it);
-
- err = reftable_reader_refs_for(reader, &it, want_hash);
- check(!err);
-
- for (j = 0; ; j++) {
- int err = reftable_iterator_next_ref(&it, &ref);
- check_int(err, >=, 0);
- if (err > 0)
- break;
- check_int(j, <, want_names_len);
- check_str(ref.refname, want_names[j]);
- reftable_ref_record_release(&ref);
- }
- check_int(j, ==, want_names_len);
-
- reftable_buf_release(&buf);
- free_names(want_names);
- reftable_iterator_destroy(&it);
- reftable_reader_decref(reader);
-}
-
-static void t_table_refs_for_no_index(void)
-{
- t_table_refs_for(0);
-}
-
-static void t_table_refs_for_obj_index(void)
-{
- t_table_refs_for(1);
-}
-
-static void t_write_empty_table(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_block_source source = { 0 };
- struct reftable_reader *rd = NULL;
- struct reftable_ref_record rec = { 0 };
- struct reftable_iterator it = { 0 };
- int err;
-
- reftable_writer_set_limits(w, 1, 1);
-
- err = reftable_writer_close(w);
- check_int(err, ==, REFTABLE_EMPTY_TABLE_ERROR);
- reftable_writer_free(w);
-
- check_uint(buf.len, ==, header_size(1) + footer_size(1));
-
- block_source_from_buf(&source, &buf);
-
- err = reftable_reader_new(&rd, &source, "filename");
- check(!err);
-
- err = reftable_reader_init_ref_iterator(rd, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, "");
- check(!err);
-
- err = reftable_iterator_next_ref(&it, &rec);
- check_int(err, >, 0);
-
- reftable_iterator_destroy(&it);
- reftable_reader_decref(rd);
- reftable_buf_release(&buf);
-}
-
-static void t_write_object_id_min_length(void)
-{
- struct reftable_write_options opts = {
- .block_size = 75,
- };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {42},
- };
- int err;
- int i;
-
- reftable_writer_set_limits(w, 1, 1);
-
- /* Write the same hash in many refs. If there is only 1 hash, the
- * disambiguating prefix is length 0 */
- for (i = 0; i < 256; i++) {
- char name[256];
- snprintf(name, sizeof(name), "ref%05d", i);
- ref.refname = name;
- err = reftable_writer_add_ref(w, &ref);
- check(!err);
- }
-
- err = reftable_writer_close(w);
- check(!err);
- check_int(reftable_writer_stats(w)->object_id_len, ==, 2);
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_write_object_id_length(void)
-{
- struct reftable_write_options opts = {
- .block_size = 75,
- };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {42},
- };
- int err;
- int i;
-
- reftable_writer_set_limits(w, 1, 1);
-
- /* Write the same hash in many refs. If there is only 1 hash, the
- * disambiguating prefix is length 0 */
- for (i = 0; i < 256; i++) {
- char name[256];
- snprintf(name, sizeof(name), "ref%05d", i);
- ref.refname = name;
- ref.value.val1[15] = i;
- err = reftable_writer_add_ref(w, &ref);
- check(!err);
- }
-
- err = reftable_writer_close(w);
- check(!err);
- check_int(reftable_writer_stats(w)->object_id_len, ==, 16);
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_write_empty_key(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_ref_record ref = {
- .refname = (char *) "",
- .update_index = 1,
- .value_type = REFTABLE_REF_DELETION,
- };
- int err;
-
- reftable_writer_set_limits(w, 1, 1);
- err = reftable_writer_add_ref(w, &ref);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- err = reftable_writer_close(w);
- check_int(err, ==, REFTABLE_EMPTY_TABLE_ERROR);
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_write_key_order(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts);
- struct reftable_ref_record refs[2] = {
- {
- .refname = (char *) "b",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value = {
- .symref = (char *) "target",
- },
- }, {
- .refname = (char *) "a",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value = {
- .symref = (char *) "target",
- },
- }
- };
- int err;
-
- reftable_writer_set_limits(w, 1, 1);
- err = reftable_writer_add_ref(w, &refs[0]);
- check(!err);
- err = reftable_writer_add_ref(w, &refs[1]);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- refs[0].update_index = 2;
- err = reftable_writer_add_ref(w, &refs[0]);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- reftable_writer_close(w);
- reftable_writer_free(w);
- reftable_buf_release(&buf);
-}
-
-static void t_write_multiple_indices(void)
-{
- struct reftable_write_options opts = {
- .block_size = 100,
- };
- struct reftable_buf writer_buf = REFTABLE_BUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_iterator it = { 0 };
- const struct reftable_stats *stats;
- struct reftable_writer *writer;
- struct reftable_reader *reader;
- char buf[128];
- int err, i;
-
- writer = t_reftable_strbuf_writer(&writer_buf, &opts);
- reftable_writer_set_limits(writer, 1, 1);
- for (i = 0; i < 100; i++) {
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {i},
- };
-
- snprintf(buf, sizeof(buf), "refs/heads/%04d", i);
- ref.refname = buf;
-
- err = reftable_writer_add_ref(writer, &ref);
- check(!err);
- }
-
- for (i = 0; i < 100; i++) {
- struct reftable_log_record log = {
- .update_index = 1,
- .value_type = REFTABLE_LOG_UPDATE,
- .value.update = {
- .old_hash = { i },
- .new_hash = { i },
- },
- };
-
- snprintf(buf, sizeof(buf), "refs/heads/%04d", i);
- log.refname = buf;
-
- err = reftable_writer_add_log(writer, &log);
- check(!err);
- }
-
- reftable_writer_close(writer);
-
- /*
- * The written data should be sufficiently large to result in indices
- * for each of the block types.
- */
- stats = reftable_writer_stats(writer);
- check_int(stats->ref_stats.index_offset, >, 0);
- check_int(stats->obj_stats.index_offset, >, 0);
- check_int(stats->log_stats.index_offset, >, 0);
-
- block_source_from_buf(&source, &writer_buf);
- err = reftable_reader_new(&reader, &source, "filename");
- check(!err);
-
- /*
- * Seeking the log uses the log index now. In case there is any
- * confusion regarding indices we would notice here.
- */
- err = reftable_reader_init_log_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_log(&it, "");
- check(!err);
-
- reftable_iterator_destroy(&it);
- reftable_writer_free(writer);
- reftable_reader_decref(reader);
- reftable_buf_release(&writer_buf);
-}
-
-static void t_write_multi_level_index(void)
-{
- struct reftable_write_options opts = {
- .block_size = 100,
- };
- struct reftable_buf writer_buf = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_iterator it = { 0 };
- const struct reftable_stats *stats;
- struct reftable_writer *writer;
- struct reftable_reader *reader;
- int err;
-
- writer = t_reftable_strbuf_writer(&writer_buf, &opts);
- reftable_writer_set_limits(writer, 1, 1);
- for (size_t i = 0; i < 200; i++) {
- struct reftable_ref_record ref = {
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {i},
- };
- char buf[128];
-
- snprintf(buf, sizeof(buf), "refs/heads/%03" PRIuMAX, (uintmax_t)i);
- ref.refname = buf;
-
- err = reftable_writer_add_ref(writer, &ref);
- check(!err);
- }
- reftable_writer_close(writer);
-
- /*
- * The written refs should be sufficiently large to result in a
- * multi-level index.
- */
- stats = reftable_writer_stats(writer);
- check_int(stats->ref_stats.max_index_level, ==, 2);
-
- block_source_from_buf(&source, &writer_buf);
- err = reftable_reader_new(&reader, &source, "filename");
- check(!err);
-
- /*
- * Seeking the last ref should work as expected.
- */
- err = reftable_reader_init_ref_iterator(reader, &it);
- check(!err);
- err = reftable_iterator_seek_ref(&it, "refs/heads/199");
- check(!err);
-
- reftable_iterator_destroy(&it);
- reftable_writer_free(writer);
- reftable_reader_decref(reader);
- reftable_buf_release(&writer_buf);
- reftable_buf_release(&buf);
-}
-
-static void t_corrupt_table_empty(void)
-{
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_reader *reader;
- int err;
-
- block_source_from_buf(&source, &buf);
- err = reftable_reader_new(&reader, &source, "file.log");
- check_int(err, ==, REFTABLE_FORMAT_ERROR);
-}
-
-static void t_corrupt_table(void)
-{
- uint8_t zeros[1024] = { 0 };
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- struct reftable_block_source source = { 0 };
- struct reftable_reader *reader;
- int err;
- check(!reftable_buf_add(&buf, zeros, sizeof(zeros)));
-
- block_source_from_buf(&source, &buf);
- err = reftable_reader_new(&reader, &source, "file.log");
- check_int(err, ==, REFTABLE_FORMAT_ERROR);
-
- reftable_buf_release(&buf);
-}
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- TEST(t_buffer(), "strbuf works as blocksource");
- TEST(t_corrupt_table(), "read-write on corrupted table");
- TEST(t_corrupt_table_empty(), "read-write on an empty table");
- TEST(t_log_buffer_size(), "buffer extension for log compression");
- TEST(t_log_overflow(), "log overflow returns expected error");
- TEST(t_log_write_limits(), "writer limits for writing log records");
- TEST(t_log_write_read(), "read-write on log records");
- TEST(t_log_zlib_corruption(), "reading corrupted log record returns expected error");
- TEST(t_table_read_api(), "read on a table");
- TEST(t_table_read_write_seek_index(), "read-write on a table with index");
- TEST(t_table_read_write_seek_linear(), "read-write on a table without index (SHA1)");
- TEST(t_table_read_write_seek_linear_sha256(), "read-write on a table without index (SHA256)");
- TEST(t_table_read_write_sequential(), "sequential read-write on a table");
- TEST(t_table_refs_for_no_index(), "refs-only table with no index");
- TEST(t_table_refs_for_obj_index(), "refs-only table with index");
- TEST(t_table_write_small_table(), "write_table works");
- TEST(t_write_empty_key(), "write on refs with empty keys");
- TEST(t_write_empty_table(), "read-write on empty tables");
- TEST(t_write_key_order(), "refs must be written in increasing order");
- TEST(t_write_multi_level_index(), "table with multi-level index");
- TEST(t_write_multiple_indices(), "table with indices for multiple block types");
- TEST(t_write_object_id_length(), "prefix compression on writing refs");
- TEST(t_write_object_id_min_length(), "prefix compression on writing refs");
-
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-readwrite.c b/t/unit-tests/u-reftable-readwrite.c
new file mode 100644
index 0000000000..3d6bdcfceb
--- /dev/null
+++ b/t/unit-tests/u-reftable-readwrite.c
@@ -0,0 +1,870 @@
+/*
+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
+*/
+
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
+#include "unit-test.h"
+#include "lib-reftable.h"
+#include "reftable/basics.h"
+#include "reftable/blocksource.h"
+#include "reftable/reader.h"
+#include "reftable/reftable-error.h"
+#include "reftable/reftable-writer.h"
+#include "strbuf.h"
+
+static const int update_index = 5;
+
+void test_reftable_readwrite__buffer(void)
+{
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_block_source source = { 0 };
+ struct reftable_block out = { 0 };
+ int n;
+ uint8_t in[] = "hello";
+ cl_assert(reftable_buf_add(&buf, in, sizeof(in)) == 0);
+ block_source_from_buf(&source, &buf);
+ cl_assert_equal_i(block_source_size(&source), 6);
+ n = block_source_read_block(&source, &out, 0, sizeof(in));
+ cl_assert_equal_i(n, sizeof(in));
+ cl_assert(memcmp(in, out.data, n) == 0);
+ reftable_block_done(&out);
+
+ cl_assert_equal_i(block_source_read_block(&source, &out, 1, 2), 2);
+ cl_assert(memcmp(out.data, "el", 2) == 0);
+
+ reftable_block_done(&out);
+ block_source_close(&source);
+ reftable_buf_release(&buf);
+}
+
+static void write_table(char ***names, struct reftable_buf *buf, int N,
+ int block_size, enum reftable_hash hash_id)
+{
+ struct reftable_write_options opts = {
+ .block_size = block_size,
+ .hash_id = hash_id,
+ };
+ struct reftable_ref_record *refs;
+ struct reftable_log_record *logs;
+ int i;
+
+ REFTABLE_CALLOC_ARRAY(*names, N + 1);
+ cl_assert(*names != NULL);
+ REFTABLE_CALLOC_ARRAY(refs, N);
+ cl_assert(refs != NULL);
+ REFTABLE_CALLOC_ARRAY(logs, N);
+ cl_assert(logs != NULL);
+
+ for (i = 0; i < N; i++) {
+ refs[i].refname = (*names)[i] = xstrfmt("refs/heads/branch%02d", i);
+ refs[i].update_index = update_index;
+ refs[i].value_type = REFTABLE_REF_VAL1;
+ cl_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1);
+ }
+
+ for (i = 0; i < N; i++) {
+ logs[i].refname = (*names)[i];
+ logs[i].update_index = update_index;
+ logs[i].value_type = REFTABLE_LOG_UPDATE;
+ cl_reftable_set_hash(logs[i].value.update.new_hash, i,
+ REFTABLE_HASH_SHA1);
+ logs[i].value.update.message = (char *) "message";
+ }
+
+ cl_reftable_write_to_buf(buf, refs, N, logs, N, &opts);
+
+ reftable_free(refs);
+ reftable_free(logs);
+}
+
+void test_reftable_readwrite__log_buffer_size(void)
+{
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_write_options opts = {
+ .block_size = 4096,
+ };
+ int i;
+ struct reftable_log_record
+ log = { .refname = (char *) "refs/heads/master",
+ .update_index = update_index,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = { .update = {
+ .name = (char *) "Han-Wen Nienhuys",
+ .email = (char *) "hanwen@google.com",
+ .tz_offset = 100,
+ .time = 0x5e430672,
+ .message = (char *) "commit: 9\n",
+ } } };
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+
+ /* This tests buffer extension for log compression. Must use a random
+ hash, to ensure that the compressed part is larger than the original.
+ */
+ for (i = 0; i < REFTABLE_HASH_SIZE_SHA1; i++) {
+ log.value.update.old_hash[i] = (uint8_t)(git_rand(0) % 256);
+ log.value.update.new_hash[i] = (uint8_t)(git_rand(0) % 256);
+ }
+ reftable_writer_set_limits(w, update_index, update_index);
+ cl_assert(reftable_writer_add_log(w, &log) == 0);
+ cl_assert(reftable_writer_close(w) == 0);
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__log_overflow(void)
+{
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ char msg[256] = { 0 };
+ struct reftable_write_options opts = {
+ .block_size = ARRAY_SIZE(msg),
+ };
+ struct reftable_log_record log = {
+ .refname = (char *) "refs/heads/master",
+ .update_index = update_index,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .old_hash = { 1 },
+ .new_hash = { 2 },
+ .name = (char *) "Han-Wen Nienhuys",
+ .email = (char *) "hanwen@google.com",
+ .tz_offset = 100,
+ .time = 0x5e430672,
+ .message = msg,
+ },
+ },
+ };
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+
+ memset(msg, 'x', sizeof(msg) - 1);
+ reftable_writer_set_limits(w, update_index, update_index);
+ cl_assert_equal_i(reftable_writer_add_log(w, &log), REFTABLE_ENTRY_TOO_BIG_ERROR);
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__log_write_limits(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_log_record log = {
+ .refname = (char *)"refs/head/master",
+ .update_index = 0,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .old_hash = { 1 },
+ .new_hash = { 2 },
+ .name = (char *)"Han-Wen Nienhuys",
+ .email = (char *)"hanwen@google.com",
+ .tz_offset = 100,
+ .time = 0x5e430672,
+ },
+ },
+ };
+
+ reftable_writer_set_limits(w, 1, 1);
+
+ /* write with update_index (0) below set limits (1, 1) */
+ cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
+
+ /* write with update_index (1) in the set limits (1, 1) */
+ log.update_index = 1;
+ cl_assert_equal_i(reftable_writer_add_log(w, &log), 0);
+
+ /* write with update_index (3) above set limits (1, 1) */
+ log.update_index = 3;
+ cl_assert_equal_i(reftable_writer_add_log(w, &log), REFTABLE_API_ERROR);
+
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__log_write_read(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ };
+ struct reftable_ref_record ref = { 0 };
+ struct reftable_log_record log = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct reftable_reader *reader;
+ struct reftable_block_source source = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ const struct reftable_stats *stats = NULL;
+ int N = 2, i;
+ char **names;
+
+ names = reftable_calloc(N + 1, sizeof(*names));
+ cl_assert(names != NULL);
+
+ reftable_writer_set_limits(w, 0, N);
+
+ for (i = 0; i < N; i++) {
+ char name[256];
+ struct reftable_ref_record ref = { 0 };
+ snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7);
+ names[i] = xstrdup(name);
+ ref.refname = name;
+ ref.update_index = i;
+
+ cl_assert(reftable_writer_add_ref(w, &ref) == 0);
+ }
+
+ for (i = 0; i < N; i++) {
+ struct reftable_log_record log = { 0 };
+
+ log.refname = names[i];
+ log.update_index = i;
+ log.value_type = REFTABLE_LOG_UPDATE;
+ cl_reftable_set_hash(log.value.update.old_hash, i,
+ REFTABLE_HASH_SHA1);
+ cl_reftable_set_hash(log.value.update.new_hash, i + 1,
+ REFTABLE_HASH_SHA1);
+
+ cl_assert(reftable_writer_add_log(w, &log) == 0);
+ }
+
+ cl_assert_equal_i(reftable_writer_close(w), 0);
+
+ stats = reftable_writer_stats(w);
+ cl_assert(stats->log_stats.blocks > 0);
+ reftable_writer_free(w);
+ w = NULL;
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "file.log") == 0);
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, names[N - 1]) == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &ref) == 0);
+
+ /* end of iteration. */
+ cl_assert(reftable_iterator_next_ref(&it, &ref) > 0);
+
+ reftable_iterator_destroy(&it);
+ reftable_ref_record_release(&ref);
+
+ cl_assert(reftable_reader_init_log_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_log(&it, "") == 0);
+
+ for (i = 0; ; i++) {
+ int err = reftable_iterator_next_log(&it, &log);
+ if (err > 0)
+ break;
+ cl_assert(err == 0);
+ cl_assert_equal_s(names[i], log.refname);
+ cl_assert_equal_i(i, log.update_index);
+ reftable_log_record_release(&log);
+ }
+
+ cl_assert_equal_i(i, N);
+ reftable_iterator_destroy(&it);
+
+ /* cleanup. */
+ reftable_buf_release(&buf);
+ free_names(names);
+ reftable_reader_decref(reader);
+}
+
+void test_reftable_readwrite__log_zlib_corruption(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ };
+ struct reftable_iterator it = { 0 };
+ struct reftable_reader *reader;
+ struct reftable_block_source source = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ const struct reftable_stats *stats = NULL;
+ char message[100] = { 0 };
+ int i;
+ struct reftable_log_record log = {
+ .refname = (char *) "refname",
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .new_hash = { 1 },
+ .old_hash = { 2 },
+ .name = (char *) "My Name",
+ .email = (char *) "myname@invalid",
+ .message = message,
+ },
+ },
+ };
+
+ for (i = 0; i < sizeof(message) - 1; i++)
+ message[i] = (uint8_t)(git_rand(0) % 64 + ' ');
+
+ reftable_writer_set_limits(w, 1, 1);
+
+ cl_assert(reftable_writer_add_log(w, &log) == 0);
+ cl_assert_equal_i(reftable_writer_close(w), 0);
+
+ stats = reftable_writer_stats(w);
+ cl_assert(stats->log_stats.blocks > 0);
+ reftable_writer_free(w);
+ w = NULL;
+
+ /* corrupt the data. */
+ buf.buf[50] ^= 0x99;
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "file.log") == 0);
+ cl_assert(reftable_reader_init_log_iterator(reader, &it) == 0);
+ cl_assert_equal_i(reftable_iterator_seek_log(&it, "refname"), REFTABLE_ZLIB_ERROR);
+
+ reftable_iterator_destroy(&it);
+
+ /* cleanup. */
+ reftable_reader_decref(reader);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__table_read_write_sequential(void)
+{
+ char **names;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ int N = 50;
+ struct reftable_iterator it = { 0 };
+ struct reftable_block_source source = { 0 };
+ struct reftable_reader *reader;
+ int j = 0;
+
+ write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1);
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "file.ref") == 0);
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+
+ for (j = 0; ; j++) {
+ struct reftable_ref_record ref = { 0 };
+ int r = reftable_iterator_next_ref(&it, &ref);
+ cl_assert(r >= 0);
+ if (r > 0)
+ break;
+ cl_assert_equal_s(names[j], ref.refname);
+ cl_assert_equal_i(update_index, ref.update_index);
+ reftable_ref_record_release(&ref);
+ }
+ cl_assert_equal_i(j, N);
+
+ reftable_iterator_destroy(&it);
+ reftable_reader_decref(reader);
+ reftable_buf_release(&buf);
+ free_names(names);
+}
+
+void test_reftable_readwrite__table_write_small_table(void)
+{
+ char **names;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ int N = 1;
+ write_table(&names, &buf, N, 4096, REFTABLE_HASH_SHA1);
+ cl_assert(buf.len < 200);
+ reftable_buf_release(&buf);
+ free_names(names);
+}
+
+void test_reftable_readwrite__table_read_api(void)
+{
+ char **names;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ int N = 50;
+ struct reftable_reader *reader;
+ struct reftable_block_source source = { 0 };
+ struct reftable_log_record log = { 0 };
+ struct reftable_iterator it = { 0 };
+
+ write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1);
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "file.ref") == 0);
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, names[0]) == 0);
+
+ cl_assert_equal_i(reftable_iterator_next_log(&it, &log), REFTABLE_API_ERROR);
+
+ reftable_buf_release(&buf);
+ free_names(names);
+ reftable_iterator_destroy(&it);
+ reftable_reader_decref(reader);
+ reftable_buf_release(&buf);
+}
+
+static void t_table_read_write_seek(int index, enum reftable_hash hash_id)
+{
+ char **names;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ int N = 50;
+ struct reftable_reader *reader;
+ struct reftable_block_source source = { 0 };
+ int err;
+ int i = 0;
+
+ struct reftable_iterator it = { 0 };
+ struct reftable_buf pastLast = REFTABLE_BUF_INIT;
+ struct reftable_ref_record ref = { 0 };
+
+ write_table(&names, &buf, N, 256, hash_id);
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "file.ref") == 0);
+ cl_assert_equal_i(hash_id, reftable_reader_hash_id(reader));
+
+ if (!index) {
+ reader->ref_offsets.index_offset = 0;
+ } else {
+ cl_assert(reader->ref_offsets.index_offset > 0);
+ }
+
+ for (i = 1; i < N; i++) {
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, names[i]) == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &ref) == 0);
+ cl_assert_equal_s(names[i], ref.refname);
+ cl_assert_equal_i(REFTABLE_REF_VAL1, ref.value_type);
+ cl_assert_equal_i(i, ref.value.val1[0]);
+
+ reftable_ref_record_release(&ref);
+ reftable_iterator_destroy(&it);
+ }
+
+ cl_assert(reftable_buf_addstr(&pastLast, names[N - 1]) == 0);
+ cl_assert(reftable_buf_addstr(&pastLast, "/") == 0);
+
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ err = reftable_iterator_seek_ref(&it, pastLast.buf);
+ if (err == 0) {
+ struct reftable_ref_record ref = { 0 };
+ int err = reftable_iterator_next_ref(&it, &ref);
+ cl_assert(err > 0);
+ } else {
+ cl_assert(err > 0);
+ }
+
+ reftable_buf_release(&pastLast);
+ reftable_iterator_destroy(&it);
+
+ reftable_buf_release(&buf);
+ free_names(names);
+ reftable_reader_decref(reader);
+}
+
+void test_reftable_readwrite__table_read_write_seek_linear(void)
+{
+ t_table_read_write_seek(0, REFTABLE_HASH_SHA1);
+}
+
+void test_reftable_readwrite__table_read_write_seek_linear_sha256(void)
+{
+ t_table_read_write_seek(0, REFTABLE_HASH_SHA256);
+}
+
+void test_reftable_readwrite__table_read_write_seek_index(void)
+{
+ t_table_read_write_seek(1, REFTABLE_HASH_SHA1);
+}
+
+static void t_table_refs_for(int indexed)
+{
+ char **want_names;
+ int want_names_len = 0;
+ uint8_t want_hash[REFTABLE_HASH_SIZE_SHA1];
+
+ struct reftable_write_options opts = {
+ .block_size = 256,
+ };
+ struct reftable_ref_record ref = { 0 };
+ struct reftable_reader *reader;
+ struct reftable_block_source source = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_iterator it = { 0 };
+ int N = 50, j, i;
+
+ want_names = reftable_calloc(N + 1, sizeof(*want_names));
+ cl_assert(want_names != NULL);
+
+ cl_reftable_set_hash(want_hash, 4, REFTABLE_HASH_SHA1);
+
+ for (i = 0; i < N; i++) {
+ uint8_t hash[REFTABLE_HASH_SIZE_SHA1];
+ char fill[51] = { 0 };
+ char name[100];
+ struct reftable_ref_record ref = { 0 };
+
+ memset(hash, i, sizeof(hash));
+ memset(fill, 'x', 50);
+ /* Put the variable part in the start */
+ snprintf(name, sizeof(name), "br%02d%s", i, fill);
+ name[40] = 0;
+ ref.refname = name;
+
+ ref.value_type = REFTABLE_REF_VAL2;
+ cl_reftable_set_hash(ref.value.val2.value, i / 4,
+ REFTABLE_HASH_SHA1);
+ cl_reftable_set_hash(ref.value.val2.target_value, 3 + i / 4,
+ REFTABLE_HASH_SHA1);
+
+ /* 80 bytes / entry, so 3 entries per block. Yields 17
+ */
+ /* blocks. */
+ cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0);
+
+ if (!memcmp(ref.value.val2.value, want_hash, REFTABLE_HASH_SIZE_SHA1) ||
+ !memcmp(ref.value.val2.target_value, want_hash, REFTABLE_HASH_SIZE_SHA1))
+ want_names[want_names_len++] = xstrdup(name);
+ }
+
+ cl_assert_equal_i(reftable_writer_close(w), 0);
+
+ reftable_writer_free(w);
+ w = NULL;
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&reader, &source, "file.ref") == 0);
+ if (!indexed)
+ reader->obj_offsets.is_present = 0;
+
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+ reftable_iterator_destroy(&it);
+
+ cl_assert(reftable_reader_refs_for(reader, &it, want_hash) == 0);
+
+ for (j = 0; ; j++) {
+ int err = reftable_iterator_next_ref(&it, &ref);
+ cl_assert(err >= 0);
+ if (err > 0)
+ break;
+ cl_assert(j < want_names_len);
+ cl_assert_equal_s(ref.refname, want_names[j]);
+ reftable_ref_record_release(&ref);
+ }
+ cl_assert_equal_i(j, want_names_len);
+
+ reftable_buf_release(&buf);
+ free_names(want_names);
+ reftable_iterator_destroy(&it);
+ reftable_reader_decref(reader);
+}
+
+void test_reftable_readwrite__table_refs_for_no_index(void)
+{
+ t_table_refs_for(0);
+}
+
+void test_reftable_readwrite__table_refs_for_obj_index(void)
+{
+ t_table_refs_for(1);
+}
+
+void test_reftable_readwrite__write_empty_table(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_block_source source = { 0 };
+ struct reftable_reader *rd = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+
+ reftable_writer_set_limits(w, 1, 1);
+
+ cl_assert_equal_i(reftable_writer_close(w), REFTABLE_EMPTY_TABLE_ERROR);
+ reftable_writer_free(w);
+
+ cl_assert_equal_i(buf.len, header_size(1) + footer_size(1));
+
+ block_source_from_buf(&source, &buf);
+
+ cl_assert(reftable_reader_new(&rd, &source, "filename") == 0);
+ cl_assert(reftable_reader_init_ref_iterator(rd, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+ cl_assert(reftable_iterator_next_ref(&it, &rec) > 0);
+
+ reftable_iterator_destroy(&it);
+ reftable_reader_decref(rd);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__write_object_id_min_length(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 75,
+ };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_ref_record ref = {
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {42},
+ };
+ int i;
+
+ reftable_writer_set_limits(w, 1, 1);
+
+ /* Write the same hash in many refs. If there is only 1 hash, the
+ * disambiguating prefix is length 0 */
+ for (i = 0; i < 256; i++) {
+ char name[256];
+ snprintf(name, sizeof(name), "ref%05d", i);
+ ref.refname = name;
+ cl_assert(reftable_writer_add_ref(w, &ref) == 0);
+ }
+
+ cl_assert(reftable_writer_close(w) == 0);
+ cl_assert_equal_i(reftable_writer_stats(w)->object_id_len, 2);
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__write_object_id_length(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 75,
+ };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_ref_record ref = {
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {42},
+ };
+ int i;
+
+ reftable_writer_set_limits(w, 1, 1);
+
+ /* Write the same hash in many refs. If there is only 1 hash, the
+ * disambiguating prefix is length 0 */
+ for (i = 0; i < 256; i++) {
+ char name[256];
+ snprintf(name, sizeof(name), "ref%05d", i);
+ ref.refname = name;
+ ref.value.val1[15] = i;
+ cl_assert(reftable_writer_add_ref(w, &ref) == 0);
+ }
+
+ cl_assert(reftable_writer_close(w) == 0);
+ cl_assert_equal_i(reftable_writer_stats(w)->object_id_len, 16);
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__write_empty_key(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_ref_record ref = {
+ .refname = (char *) "",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_DELETION,
+ };
+
+ reftable_writer_set_limits(w, 1, 1);
+ cl_assert_equal_i(reftable_writer_add_ref(w, &ref), REFTABLE_API_ERROR);
+ cl_assert_equal_i(reftable_writer_close(w), REFTABLE_EMPTY_TABLE_ERROR);
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__write_key_order(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts);
+ struct reftable_ref_record refs[2] = {
+ {
+ .refname = (char *) "b",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value = {
+ .symref = (char *) "target",
+ },
+ }, {
+ .refname = (char *) "a",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value = {
+ .symref = (char *) "target",
+ },
+ }
+ };
+
+ reftable_writer_set_limits(w, 1, 1);
+ cl_assert(reftable_writer_add_ref(w, &refs[0]) == 0);
+ cl_assert_equal_i(reftable_writer_add_ref(w, &refs[1]), REFTABLE_API_ERROR);
+
+ refs[0].update_index = 2;
+ cl_assert_equal_i(reftable_writer_add_ref(w, &refs[0]), REFTABLE_API_ERROR);
+
+ reftable_writer_close(w);
+ reftable_writer_free(w);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__write_multiple_indices(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 100,
+ };
+ struct reftable_buf writer_buf = REFTABLE_BUF_INIT;
+ struct reftable_block_source source = { 0 };
+ struct reftable_iterator it = { 0 };
+ const struct reftable_stats *stats;
+ struct reftable_writer *writer;
+ struct reftable_reader *reader;
+ char buf[128];
+ int i;
+
+ writer = cl_reftable_strbuf_writer(&writer_buf, &opts);
+ reftable_writer_set_limits(writer, 1, 1);
+ for (i = 0; i < 100; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {i},
+ };
+
+ snprintf(buf, sizeof(buf), "refs/heads/%04d", i);
+ ref.refname = buf;
+
+ cl_assert(reftable_writer_add_ref(writer, &ref) == 0);
+ }
+
+ for (i = 0; i < 100; i++) {
+ struct reftable_log_record log = {
+ .update_index = 1,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value.update = {
+ .old_hash = { i },
+ .new_hash = { i },
+ },
+ };
+
+ snprintf(buf, sizeof(buf), "refs/heads/%04d", i);
+ log.refname = buf;
+
+ cl_assert(reftable_writer_add_log(writer, &log) == 0);
+ }
+
+ reftable_writer_close(writer);
+
+ /*
+ * The written data should be sufficiently large to result in indices
+ * for each of the block types.
+ */
+ stats = reftable_writer_stats(writer);
+ cl_assert(stats->ref_stats.index_offset > 0);
+ cl_assert(stats->obj_stats.index_offset > 0);
+ cl_assert(stats->log_stats.index_offset > 0);
+
+ block_source_from_buf(&source, &writer_buf);
+ cl_assert(reftable_reader_new(&reader, &source, "filename") == 0);
+
+ /*
+ * Seeking the log uses the log index now. In case there is any
+ * confusion regarding indices we would notice here.
+ */
+ cl_assert(reftable_reader_init_log_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_log(&it, "") == 0);
+
+ reftable_iterator_destroy(&it);
+ reftable_writer_free(writer);
+ reftable_reader_decref(reader);
+ reftable_buf_release(&writer_buf);
+}
+
+void test_reftable_readwrite__write_multi_level_index(void)
+{
+ struct reftable_write_options opts = {
+ .block_size = 100,
+ };
+ struct reftable_buf writer_buf = REFTABLE_BUF_INIT, buf = REFTABLE_BUF_INIT;
+ struct reftable_block_source source = { 0 };
+ struct reftable_iterator it = { 0 };
+ const struct reftable_stats *stats;
+ struct reftable_writer *writer;
+ struct reftable_reader *reader;
+
+ writer = cl_reftable_strbuf_writer(&writer_buf, &opts);
+ reftable_writer_set_limits(writer, 1, 1);
+ for (size_t i = 0; i < 200; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {i},
+ };
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "refs/heads/%03" PRIuMAX, (uintmax_t)i);
+ ref.refname = buf;
+
+ cl_assert(reftable_writer_add_ref(writer, &ref) == 0);
+ }
+ reftable_writer_close(writer);
+
+ /*
+ * The written refs should be sufficiently large to result in a
+ * multi-level index.
+ */
+ stats = reftable_writer_stats(writer);
+ cl_assert_equal_i(stats->ref_stats.max_index_level, 2);
+
+ block_source_from_buf(&source, &writer_buf);
+ cl_assert(reftable_reader_new(&reader, &source, "filename") == 0);
+
+ /*
+ * Seeking the last ref should work as expected.
+ */
+ cl_assert(reftable_reader_init_ref_iterator(reader, &it) == 0);
+ cl_assert(reftable_iterator_seek_ref(&it, "refs/heads/199") == 0);
+
+ reftable_iterator_destroy(&it);
+ reftable_writer_free(writer);
+ reftable_reader_decref(reader);
+ reftable_buf_release(&writer_buf);
+ reftable_buf_release(&buf);
+}
+
+void test_reftable_readwrite__corrupt_table_empty(void)
+{
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_block_source source = { 0 };
+ struct reftable_reader *reader;
+
+ block_source_from_buf(&source, &buf);
+ cl_assert_equal_i(reftable_reader_new(&reader, &source,
+ "file.log"),REFTABLE_FORMAT_ERROR);
+}
+
+void test_reftable_readwrite__corrupt_table(void)
+{
+ uint8_t zeros[1024] = { 0 };
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ struct reftable_block_source source = { 0 };
+ struct reftable_reader *reader;
+ cl_assert(reftable_buf_add(&buf, zeros, sizeof(zeros)) == 0);
+
+ block_source_from_buf(&source, &buf);
+ cl_assert_equal_i(reftable_reader_new(&reader, &source,
+ "file.log"), REFTABLE_FORMAT_ERROR);
+
+ reftable_buf_release(&buf);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 07/10] t/unit-tests: convert reftable readwrite test to use clar
2025-04-29 17:52 ` [PATCH v2 07/10] t/unit-tests: convert reftable readwrite " Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:52:59PM +0100, Seyi Kuforiji wrote:
> diff --git a/t/unit-tests/u-reftable-readwrite.c b/t/unit-tests/u-reftable-readwrite.c
> new file mode 100644
> index 0000000000..3d6bdcfceb
> --- /dev/null
> +++ b/t/unit-tests/u-reftable-readwrite.c
> @@ -0,0 +1,870 @@
> +/*
> +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
> +*/
> +
> +#define DISABLE_SIGN_COMPARE_WARNINGS
> +
> +#include "unit-test.h"
> +#include "lib-reftable.h"
> +#include "reftable/basics.h"
> +#include "reftable/blocksource.h"
> +#include "reftable/reader.h"
> +#include "reftable/reftable-error.h"
> +#include "reftable/reftable-writer.h"
> +#include "strbuf.h"
> +
> +static const int update_index = 5;
> +
> +void test_reftable_readwrite__buffer(void)
> +{
> + struct reftable_buf buf = REFTABLE_BUF_INIT;
> + struct reftable_block_source source = { 0 };
> + struct reftable_block out = { 0 };
> + int n;
> + uint8_t in[] = "hello";
> + cl_assert(reftable_buf_add(&buf, in, sizeof(in)) == 0);
> + block_source_from_buf(&source, &buf);
> + cl_assert_equal_i(block_source_size(&source), 6);
> + n = block_source_read_block(&source, &out, 0, sizeof(in));
> + cl_assert_equal_i(n, sizeof(in));
> + cl_assert(memcmp(in, out.data, n) == 0);
It feels inconsisetnt that we use `cl_assert_equal_i()` to check for `n`
but `cl_assert(... == 0)` here.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 08/10] t/unit-tests: convert reftable record test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (6 preceding siblings ...)
2025-04-29 17:52 ` [PATCH v2 07/10] t/unit-tests: convert reftable readwrite " Seyi Kuforiji
@ 2025-04-29 17:53 ` Seyi Kuforiji
2025-04-29 17:53 ` [PATCH v2 09/10] t/unit-tests: convert reftable stack " Seyi Kuforiji
2025-04-29 17:53 ` [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar Seyi Kuforiji
9 siblings, 0 replies; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:53 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable record test file to use clar by using clar assertions
where necessary.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 2 +-
t/unit-tests/t-reftable-record.c | 585 -------------------------------
t/unit-tests/u-reftable-record.c | 565 +++++++++++++++++++++++++++++
4 files changed, 567 insertions(+), 587 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-record.c
create mode 100644 t/unit-tests/u-reftable-record.c
diff --git a/Makefile b/Makefile
index 88b6851b37..cd9db9adf1 100644
--- a/Makefile
+++ b/Makefile
@@ -1368,6 +1368,7 @@ CLAR_TEST_SUITES += u-reftable-merged
CLAR_TEST_SUITES += u-reftable-pq
CLAR_TEST_SUITES += u-reftable-reader
CLAR_TEST_SUITES += u-reftable-readwrite
+CLAR_TEST_SUITES += u-reftable-record
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1380,7 +1381,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-record
UNIT_TEST_PROGRAMS += t-reftable-stack
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
diff --git a/t/meson.build b/t/meson.build
index 7722d177e2..756cb2a2dd 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -14,6 +14,7 @@ clar_test_suites = [
'unit-tests/u-reftable-pq.c',
'unit-tests/u-reftable-reader.c',
'unit-tests/u-reftable-readwrite.c',
+ 'unit-tests/u-reftable-record.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -60,7 +61,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-record.c',
'unit-tests/t-reftable-stack.c',
]
diff --git a/t/unit-tests/t-reftable-record.c b/t/unit-tests/t-reftable-record.c
deleted file mode 100644
index 5954966373..0000000000
--- a/t/unit-tests/t-reftable-record.c
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- 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/basics.h"
-#include "reftable/constants.h"
-#include "reftable/record.h"
-
-static void t_copy(struct reftable_record *rec)
-{
- struct reftable_record copy;
- uint8_t typ;
-
- typ = reftable_record_type(rec);
- check(!reftable_record_init(©, typ));
- reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1);
- /* do it twice to catch memory leaks */
- reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1);
- check(reftable_record_equal(rec, ©, REFTABLE_HASH_SIZE_SHA1));
-
- reftable_record_release(©);
-}
-
-static void t_varint_roundtrip(void)
-{
- uint64_t inputs[] = { 0,
- 1,
- 27,
- 127,
- 128,
- 257,
- 4096,
- ((uint64_t)1 << 63),
- ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 };
-
- for (size_t i = 0; i < ARRAY_SIZE(inputs); i++) {
- uint8_t dest[10];
-
- struct string_view out = {
- .buf = dest,
- .len = sizeof(dest),
- };
- uint64_t in = inputs[i];
- int n = put_var_int(&out, in);
- uint64_t got = 0;
-
- check_int(n, >, 0);
- out.len = n;
- n = get_var_int(&got, &out);
- check_int(n, >, 0);
-
- check_int(got, ==, in);
- }
-}
-
-static void t_varint_overflow(void)
-{
- unsigned char buf[] = {
- 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x00,
- };
- struct string_view view = {
- .buf = buf,
- .len = sizeof(buf),
- };
- uint64_t value;
- int err = get_var_int(&value, &view);
- check_int(err, ==, -1);
-}
-
-static void set_hash(uint8_t *h, int j)
-{
- for (size_t i = 0; i < hash_size(REFTABLE_HASH_SHA1); i++)
- h[i] = (j >> i) & 0xff;
-}
-
-static void t_reftable_ref_record_comparison(void)
-{
- struct reftable_record in[3] = {
- {
- .type = BLOCK_TYPE_REF,
- .u.ref.refname = (char *) "refs/heads/master",
- .u.ref.value_type = REFTABLE_REF_VAL1,
- },
- {
- .type = BLOCK_TYPE_REF,
- .u.ref.refname = (char *) "refs/heads/master",
- .u.ref.value_type = REFTABLE_REF_DELETION,
- },
- {
- .type = BLOCK_TYPE_REF,
- .u.ref.refname = (char *) "HEAD",
- .u.ref.value_type = REFTABLE_REF_SYMREF,
- .u.ref.value.symref = (char *) "refs/heads/master",
- },
- };
- int cmp;
-
- check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check(!cmp);
-
- check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[1], &in[2], &cmp));
- check_int(cmp, >, 0);
-
- in[1].u.ref.value_type = in[0].u.ref.value_type;
- check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check(!cmp);
-}
-
-static void t_reftable_ref_record_compare_name(void)
-{
- struct reftable_ref_record recs[3] = {
- {
- .refname = (char *) "refs/heads/a"
- },
- {
- .refname = (char *) "refs/heads/b"
- },
- {
- .refname = (char *) "refs/heads/a"
- },
- };
-
- check_int(reftable_ref_record_compare_name(&recs[0], &recs[1]), <, 0);
- check_int(reftable_ref_record_compare_name(&recs[1], &recs[0]), >, 0);
- check_int(reftable_ref_record_compare_name(&recs[0], &recs[2]), ==, 0);
-}
-
-static void t_reftable_ref_record_roundtrip(void)
-{
- struct reftable_buf scratch = REFTABLE_BUF_INIT;
-
- for (int i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
- struct reftable_record in = {
- .type = BLOCK_TYPE_REF,
- .u.ref.value_type = i,
- };
- struct reftable_record out = { .type = BLOCK_TYPE_REF };
- struct reftable_buf key = REFTABLE_BUF_INIT;
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- int n, m;
-
- in.u.ref.value_type = i;
- switch (i) {
- case REFTABLE_REF_DELETION:
- break;
- case REFTABLE_REF_VAL1:
- set_hash(in.u.ref.value.val1, 1);
- break;
- case REFTABLE_REF_VAL2:
- set_hash(in.u.ref.value.val2.value, 1);
- set_hash(in.u.ref.value.val2.target_value, 2);
- break;
- case REFTABLE_REF_SYMREF:
- in.u.ref.value.symref = xstrdup("target");
- break;
- }
- in.u.ref.refname = xstrdup("refs/heads/master");
-
- t_copy(&in);
-
- check_int(reftable_record_val_type(&in), ==, i);
- check_int(reftable_record_is_deletion(&in), ==, i == REFTABLE_REF_DELETION);
-
- reftable_record_key(&in, &key);
- n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
- check_int(n, >, 0);
-
- /* decode into a non-zero reftable_record to test for leaks. */
- m = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch);
- check_int(n, ==, m);
-
- check(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
- REFTABLE_HASH_SIZE_SHA1));
- reftable_record_release(&in);
-
- reftable_buf_release(&key);
- reftable_record_release(&out);
- }
-
- reftable_buf_release(&scratch);
-}
-
-static void t_reftable_log_record_comparison(void)
-{
- struct reftable_record in[3] = {
- {
- .type = BLOCK_TYPE_LOG,
- .u.log.refname = (char *) "refs/heads/master",
- .u.log.update_index = 42,
- },
- {
- .type = BLOCK_TYPE_LOG,
- .u.log.refname = (char *) "refs/heads/master",
- .u.log.update_index = 22,
- },
- {
- .type = BLOCK_TYPE_LOG,
- .u.log.refname = (char *) "refs/heads/main",
- .u.log.update_index = 22,
- },
- };
- int cmp;
-
- check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[1], &in[2], &cmp));
- check_int(cmp, >, 0);
- /* comparison should be reversed for equal keys, because
- * comparison is now performed on the basis of update indices */
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check_int(cmp, <, 0);
-
- in[1].u.log.update_index = in[0].u.log.update_index;
- check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
-}
-
-static void t_reftable_log_record_compare_key(void)
-{
- struct reftable_log_record logs[3] = {
- {
- .refname = (char *) "refs/heads/a",
- .update_index = 1,
- },
- {
- .refname = (char *) "refs/heads/b",
- .update_index = 2,
- },
- {
- .refname = (char *) "refs/heads/a",
- .update_index = 3,
- },
- };
-
- check_int(reftable_log_record_compare_key(&logs[0], &logs[1]), <, 0);
- check_int(reftable_log_record_compare_key(&logs[1], &logs[0]), >, 0);
-
- logs[1].update_index = logs[0].update_index;
- check_int(reftable_log_record_compare_key(&logs[0], &logs[1]), <, 0);
-
- check_int(reftable_log_record_compare_key(&logs[0], &logs[2]), >, 0);
- check_int(reftable_log_record_compare_key(&logs[2], &logs[0]), <, 0);
- logs[2].update_index = logs[0].update_index;
- check_int(reftable_log_record_compare_key(&logs[0], &logs[2]), ==, 0);
-}
-
-static void t_reftable_log_record_roundtrip(void)
-{
- struct reftable_log_record in[] = {
- {
- .refname = xstrdup("refs/heads/master"),
- .update_index = 42,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .name = xstrdup("han-wen"),
- .email = xstrdup("hanwen@google.com"),
- .message = xstrdup("test"),
- .time = 1577123507,
- .tz_offset = 100,
- },
- }
- },
- {
- .refname = xstrdup("refs/heads/master"),
- .update_index = 22,
- .value_type = REFTABLE_LOG_DELETION,
- },
- {
- .refname = xstrdup("branch"),
- .update_index = 33,
- .value_type = REFTABLE_LOG_UPDATE,
- }
- };
- struct reftable_buf scratch = REFTABLE_BUF_INIT;
- set_hash(in[0].value.update.new_hash, 1);
- set_hash(in[0].value.update.old_hash, 2);
- set_hash(in[2].value.update.new_hash, 3);
- set_hash(in[2].value.update.old_hash, 4);
-
- check(!reftable_log_record_is_deletion(&in[0]));
- check(reftable_log_record_is_deletion(&in[1]));
- check(!reftable_log_record_is_deletion(&in[2]));
-
- for (size_t i = 0; i < ARRAY_SIZE(in); i++) {
- struct reftable_record rec = { .type = BLOCK_TYPE_LOG };
- struct reftable_buf key = REFTABLE_BUF_INIT;
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- /* populate out, to check for leaks. */
- struct reftable_record out = {
- .type = BLOCK_TYPE_LOG,
- .u.log = {
- .refname = xstrdup("old name"),
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .name = xstrdup("old name"),
- .email = xstrdup("old@email"),
- .message = xstrdup("old message"),
- },
- },
- },
- };
- int n, m, valtype;
-
- rec.u.log = in[i];
-
- t_copy(&rec);
-
- reftable_record_key(&rec, &key);
-
- n = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1);
- check_int(n, >=, 0);
- valtype = reftable_record_val_type(&rec);
- m = reftable_record_decode(&out, key, valtype, dest,
- REFTABLE_HASH_SIZE_SHA1, &scratch);
- check_int(n, ==, m);
-
- check(reftable_log_record_equal(&in[i], &out.u.log,
- REFTABLE_HASH_SIZE_SHA1));
- reftable_log_record_release(&in[i]);
- reftable_buf_release(&key);
- reftable_record_release(&out);
- }
-
- reftable_buf_release(&scratch);
-}
-
-static void t_key_roundtrip(void)
-{
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- struct reftable_buf last_key = REFTABLE_BUF_INIT;
- struct reftable_buf key = REFTABLE_BUF_INIT;
- struct reftable_buf roundtrip = REFTABLE_BUF_INIT;
- int restart;
- uint8_t extra;
- int n, m;
- uint8_t rt_extra;
-
- check(!reftable_buf_addstr(&last_key, "refs/heads/master"));
- check(!reftable_buf_addstr(&key, "refs/tags/bla"));
- extra = 6;
- n = reftable_encode_key(&restart, dest, last_key, key, extra);
- check(!restart);
- check_int(n, >, 0);
-
- check(!reftable_buf_addstr(&roundtrip, "refs/heads/master"));
- m = reftable_decode_key(&roundtrip, &rt_extra, dest);
- check_int(n, ==, m);
- check(!reftable_buf_cmp(&key, &roundtrip));
- check_int(rt_extra, ==, extra);
-
- reftable_buf_release(&last_key);
- reftable_buf_release(&key);
- reftable_buf_release(&roundtrip);
-}
-
-static void t_reftable_obj_record_comparison(void)
-{
-
- uint8_t id_bytes[] = { 0, 1, 2, 3, 4, 5, 6 };
- uint64_t offsets[] = { 0, 16, 32, 48, 64, 80, 96, 112};
- struct reftable_record in[3] = {
- {
- .type = BLOCK_TYPE_OBJ,
- .u.obj.hash_prefix = id_bytes,
- .u.obj.hash_prefix_len = 7,
- .u.obj.offsets = offsets,
- .u.obj.offset_len = 8,
- },
- {
- .type = BLOCK_TYPE_OBJ,
- .u.obj.hash_prefix = id_bytes,
- .u.obj.hash_prefix_len = 7,
- .u.obj.offsets = offsets,
- .u.obj.offset_len = 5,
- },
- {
- .type = BLOCK_TYPE_OBJ,
- .u.obj.hash_prefix = id_bytes,
- .u.obj.hash_prefix_len = 5,
- },
- };
- int cmp;
-
- check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check(!cmp);
-
- check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[1], &in[2], &cmp));
- check_int(cmp, >, 0);
-
- in[1].u.obj.offset_len = in[0].u.obj.offset_len;
- check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check(!cmp);
-}
-
-static void t_reftable_obj_record_roundtrip(void)
-{
- uint8_t testHash1[REFTABLE_HASH_SIZE_SHA1] = { 1, 2, 3, 4, 0 };
- uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
- struct reftable_obj_record recs[3] = {
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- .offsets = till9,
- .offset_len = 3,
- },
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- .offsets = till9,
- .offset_len = 9,
- },
- {
- .hash_prefix = testHash1,
- .hash_prefix_len = 5,
- },
- };
- struct reftable_buf scratch = REFTABLE_BUF_INIT;
-
- for (size_t i = 0; i < ARRAY_SIZE(recs); i++) {
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- struct reftable_record in = {
- .type = BLOCK_TYPE_OBJ,
- .u = {
- .obj = recs[i],
- },
- };
- struct reftable_buf key = REFTABLE_BUF_INIT;
- struct reftable_record out = { .type = BLOCK_TYPE_OBJ };
- int n, m;
- uint8_t extra;
-
- check(!reftable_record_is_deletion(&in));
- t_copy(&in);
- reftable_record_key(&in, &key);
- n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
- check_int(n, >, 0);
- extra = reftable_record_val_type(&in);
- m = reftable_record_decode(&out, key, extra, dest,
- REFTABLE_HASH_SIZE_SHA1, &scratch);
- check_int(n, ==, m);
-
- check(reftable_record_equal(&in, &out, REFTABLE_HASH_SIZE_SHA1));
- reftable_buf_release(&key);
- reftable_record_release(&out);
- }
-
- reftable_buf_release(&scratch);
-}
-
-static void t_reftable_index_record_comparison(void)
-{
- struct reftable_record in[3] = {
- {
- .type = BLOCK_TYPE_INDEX,
- .u.idx.offset = 22,
- .u.idx.last_key = REFTABLE_BUF_INIT,
- },
- {
- .type = BLOCK_TYPE_INDEX,
- .u.idx.offset = 32,
- .u.idx.last_key = REFTABLE_BUF_INIT,
- },
- {
- .type = BLOCK_TYPE_INDEX,
- .u.idx.offset = 32,
- .u.idx.last_key = REFTABLE_BUF_INIT,
- },
- };
- int cmp;
-
- check(!reftable_buf_addstr(&in[0].u.idx.last_key, "refs/heads/master"));
- check(!reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master"));
- check(!reftable_buf_addstr(&in[2].u.idx.last_key, "refs/heads/branch"));
-
- check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check(!cmp);
-
- check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[1], &in[2], &cmp));
- check_int(cmp, >, 0);
-
- in[1].u.idx.offset = in[0].u.idx.offset;
- check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1], &cmp));
- check(!cmp);
-
- for (size_t i = 0; i < ARRAY_SIZE(in); i++)
- reftable_record_release(&in[i]);
-}
-
-static void t_reftable_index_record_roundtrip(void)
-{
- struct reftable_record in = {
- .type = BLOCK_TYPE_INDEX,
- .u.idx = {
- .offset = 42,
- .last_key = REFTABLE_BUF_INIT,
- },
- };
- uint8_t buffer[1024] = { 0 };
- struct string_view dest = {
- .buf = buffer,
- .len = sizeof(buffer),
- };
- struct reftable_buf scratch = REFTABLE_BUF_INIT;
- struct reftable_buf key = REFTABLE_BUF_INIT;
- struct reftable_record out = {
- .type = BLOCK_TYPE_INDEX,
- .u.idx = { .last_key = REFTABLE_BUF_INIT },
- };
- int n, m;
- uint8_t extra;
-
- check(!reftable_buf_addstr(&in.u.idx.last_key, "refs/heads/master"));
- reftable_record_key(&in, &key);
- t_copy(&in);
-
- check(!reftable_record_is_deletion(&in));
- check(!reftable_buf_cmp(&key, &in.u.idx.last_key));
- n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
- check_int(n, >, 0);
-
- extra = reftable_record_val_type(&in);
- m = reftable_record_decode(&out, key, extra, dest, REFTABLE_HASH_SIZE_SHA1,
- &scratch);
- check_int(m, ==, n);
-
- check(reftable_record_equal(&in, &out, REFTABLE_HASH_SIZE_SHA1));
-
- reftable_record_release(&out);
- reftable_buf_release(&key);
- reftable_buf_release(&scratch);
- reftable_buf_release(&in.u.idx.last_key);
-}
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- TEST(t_reftable_ref_record_comparison(), "comparison operations work on ref record");
- TEST(t_reftable_log_record_comparison(), "comparison operations work on log record");
- TEST(t_reftable_index_record_comparison(), "comparison operations work on index record");
- TEST(t_reftable_obj_record_comparison(), "comparison operations work on obj record");
- TEST(t_reftable_ref_record_compare_name(), "reftable_ref_record_compare_name works");
- TEST(t_reftable_log_record_compare_key(), "reftable_log_record_compare_key works");
- TEST(t_reftable_log_record_roundtrip(), "record operations work on log record");
- TEST(t_reftable_ref_record_roundtrip(), "record operations work on ref record");
- TEST(t_varint_roundtrip(), "put_var_int and get_var_int work");
- TEST(t_varint_overflow(), "get_var_int notices an integer overflow");
- TEST(t_key_roundtrip(), "reftable_encode_key and reftable_decode_key work");
- TEST(t_reftable_obj_record_roundtrip(), "record operations work on obj record");
- TEST(t_reftable_index_record_roundtrip(), "record operations work on index record");
-
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-record.c b/t/unit-tests/u-reftable-record.c
new file mode 100644
index 0000000000..ac2e33584c
--- /dev/null
+++ b/t/unit-tests/u-reftable-record.c
@@ -0,0 +1,565 @@
+/*
+ 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 "unit-test.h"
+#include "reftable/basics.h"
+#include "reftable/constants.h"
+#include "reftable/record.h"
+
+static void t_copy(struct reftable_record *rec)
+{
+ struct reftable_record copy;
+ uint8_t typ;
+
+ typ = reftable_record_type(rec);
+ cl_assert(reftable_record_init(©, typ) == 0);
+ reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1);
+ /* do it twice to catch memory leaks */
+ reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1);
+ cl_assert(reftable_record_equal(rec, ©, REFTABLE_HASH_SIZE_SHA1) != 0);
+
+ reftable_record_release(©);
+}
+
+void test_reftable_record__varint_roundtrip(void)
+{
+ uint64_t inputs[] = { 0,
+ 1,
+ 27,
+ 127,
+ 128,
+ 257,
+ 4096,
+ ((uint64_t)1 << 63),
+ ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 };
+
+ for (size_t i = 0; i < ARRAY_SIZE(inputs); i++) {
+ uint8_t dest[10];
+
+ struct string_view out = {
+ .buf = dest,
+ .len = sizeof(dest),
+ };
+ uint64_t in = inputs[i];
+ int n = put_var_int(&out, in);
+ uint64_t got = 0;
+
+ cl_assert(n > 0);
+ out.len = n;
+ n = get_var_int(&got, &out);
+ cl_assert(n > 0);
+
+ cl_assert_equal_i(got, in);
+ }
+}
+
+void test_reftable_record__varint_overflow(void)
+{
+ unsigned char buf[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00,
+ };
+ struct string_view view = {
+ .buf = buf,
+ .len = sizeof(buf),
+ };
+ uint64_t value;
+ cl_assert_equal_i(get_var_int(&value, &view), -1);
+}
+
+static void set_hash(uint8_t *h, int j)
+{
+ for (size_t i = 0; i < hash_size(REFTABLE_HASH_SHA1); i++)
+ h[i] = (j >> i) & 0xff;
+}
+
+void test_reftable_record__ref_record_comparison(void)
+{
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.refname = (char *) "refs/heads/master",
+ .u.ref.value_type = REFTABLE_REF_VAL1,
+ },
+ {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.refname = (char *) "refs/heads/master",
+ .u.ref.value_type = REFTABLE_REF_DELETION,
+ },
+ {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.refname = (char *) "HEAD",
+ .u.ref.value_type = REFTABLE_REF_SYMREF,
+ .u.ref.value.symref = (char *) "refs/heads/master",
+ },
+ };
+ int cmp;
+
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp == 0);
+
+ cl_assert(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[1], &in[2], &cmp) == 0);
+ cl_assert(cmp > 0);
+
+ in[1].u.ref.value_type = in[0].u.ref.value_type;
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp == 0);
+}
+
+void test_reftable_record__ref_record_compare_name(void)
+{
+ struct reftable_ref_record recs[3] = {
+ {
+ .refname = (char *) "refs/heads/a"
+ },
+ {
+ .refname = (char *) "refs/heads/b"
+ },
+ {
+ .refname = (char *) "refs/heads/a"
+ },
+ };
+
+ cl_assert(reftable_ref_record_compare_name(&recs[0], &recs[1]) < 0);
+ cl_assert(reftable_ref_record_compare_name(&recs[1], &recs[0]) > 0);
+ cl_assert_equal_i(reftable_ref_record_compare_name(&recs[0], &recs[2]), 0);
+}
+
+void test_reftable_record__ref_record_roundtrip(void)
+{
+ struct reftable_buf scratch = REFTABLE_BUF_INIT;
+
+ for (int i = REFTABLE_REF_DELETION; i < REFTABLE_NR_REF_VALUETYPES; i++) {
+ struct reftable_record in = {
+ .type = BLOCK_TYPE_REF,
+ .u.ref.value_type = i,
+ };
+ struct reftable_record out = { .type = BLOCK_TYPE_REF };
+ struct reftable_buf key = REFTABLE_BUF_INIT;
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ int n, m;
+
+ in.u.ref.value_type = i;
+ switch (i) {
+ case REFTABLE_REF_DELETION:
+ break;
+ case REFTABLE_REF_VAL1:
+ set_hash(in.u.ref.value.val1, 1);
+ break;
+ case REFTABLE_REF_VAL2:
+ set_hash(in.u.ref.value.val2.value, 1);
+ set_hash(in.u.ref.value.val2.target_value, 2);
+ break;
+ case REFTABLE_REF_SYMREF:
+ in.u.ref.value.symref = xstrdup("target");
+ break;
+ }
+ in.u.ref.refname = xstrdup("refs/heads/master");
+
+ t_copy(&in);
+
+ cl_assert_equal_i(reftable_record_val_type(&in), i);
+ cl_assert_equal_i(reftable_record_is_deletion(&in), i == REFTABLE_REF_DELETION);
+
+ reftable_record_key(&in, &key);
+ n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
+ cl_assert(n > 0);
+
+ /* decode into a non-zero reftable_record to test for leaks. */
+ m = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch);
+ cl_assert_equal_i(n, m);
+
+ cl_assert(reftable_ref_record_equal(&in.u.ref, &out.u.ref,
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_record_release(&in);
+
+ reftable_buf_release(&key);
+ reftable_record_release(&out);
+ }
+
+ reftable_buf_release(&scratch);
+}
+
+void test_reftable_record__log_record_comparison(void)
+{
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_LOG,
+ .u.log.refname = (char *) "refs/heads/master",
+ .u.log.update_index = 42,
+ },
+ {
+ .type = BLOCK_TYPE_LOG,
+ .u.log.refname = (char *) "refs/heads/master",
+ .u.log.update_index = 22,
+ },
+ {
+ .type = BLOCK_TYPE_LOG,
+ .u.log.refname = (char *) "refs/heads/main",
+ .u.log.update_index = 22,
+ },
+ };
+ int cmp;
+
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[1], &in[2], &cmp) == 0);
+ cl_assert(cmp > 0);
+ /* comparison should be reversed for equal keys, because
+ * comparison is now performed on the basis of update indices */
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp < 0);
+
+ in[1].u.log.update_index = in[0].u.log.update_index;
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+}
+
+void test_reftable_record__log_record_compare_key(void)
+{
+ struct reftable_log_record logs[3] = {
+ {
+ .refname = (char *) "refs/heads/a",
+ .update_index = 1,
+ },
+ {
+ .refname = (char *) "refs/heads/b",
+ .update_index = 2,
+ },
+ {
+ .refname = (char *) "refs/heads/a",
+ .update_index = 3,
+ },
+ };
+
+ cl_assert(reftable_log_record_compare_key(&logs[0], &logs[1]) < 0);
+ cl_assert(reftable_log_record_compare_key(&logs[1], &logs[0]) > 0);
+
+ logs[1].update_index = logs[0].update_index;
+ cl_assert(reftable_log_record_compare_key(&logs[0], &logs[1]) < 0);
+
+ cl_assert(reftable_log_record_compare_key(&logs[0], &logs[2]) > 0);
+ cl_assert(reftable_log_record_compare_key(&logs[2], &logs[0]) < 0);
+ logs[2].update_index = logs[0].update_index;
+ cl_assert_equal_i(reftable_log_record_compare_key(&logs[0], &logs[2]), 0);
+}
+
+void test_reftable_record__log_record_roundtrip(void)
+{
+ struct reftable_log_record in[] = {
+ {
+ .refname = xstrdup("refs/heads/master"),
+ .update_index = 42,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .name = xstrdup("han-wen"),
+ .email = xstrdup("hanwen@google.com"),
+ .message = xstrdup("test"),
+ .time = 1577123507,
+ .tz_offset = 100,
+ },
+ }
+ },
+ {
+ .refname = xstrdup("refs/heads/master"),
+ .update_index = 22,
+ .value_type = REFTABLE_LOG_DELETION,
+ },
+ {
+ .refname = xstrdup("branch"),
+ .update_index = 33,
+ .value_type = REFTABLE_LOG_UPDATE,
+ }
+ };
+ struct reftable_buf scratch = REFTABLE_BUF_INIT;
+ set_hash(in[0].value.update.new_hash, 1);
+ set_hash(in[0].value.update.old_hash, 2);
+ set_hash(in[2].value.update.new_hash, 3);
+ set_hash(in[2].value.update.old_hash, 4);
+
+ cl_assert(reftable_log_record_is_deletion(&in[0]) == 0);
+ cl_assert(reftable_log_record_is_deletion(&in[1]) != 0);
+ cl_assert(reftable_log_record_is_deletion(&in[2]) == 0);
+
+ for (size_t i = 0; i < ARRAY_SIZE(in); i++) {
+ struct reftable_record rec = { .type = BLOCK_TYPE_LOG };
+ struct reftable_buf key = REFTABLE_BUF_INIT;
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ /* populate out, to check for leaks. */
+ struct reftable_record out = {
+ .type = BLOCK_TYPE_LOG,
+ .u.log = {
+ .refname = xstrdup("old name"),
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .name = xstrdup("old name"),
+ .email = xstrdup("old@email"),
+ .message = xstrdup("old message"),
+ },
+ },
+ },
+ };
+ int n, m, valtype;
+
+ rec.u.log = in[i];
+
+ t_copy(&rec);
+
+ reftable_record_key(&rec, &key);
+
+ n = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1);
+ cl_assert(n >= 0);
+ valtype = reftable_record_val_type(&rec);
+ m = reftable_record_decode(&out, key, valtype, dest,
+ REFTABLE_HASH_SIZE_SHA1, &scratch);
+ cl_assert_equal_i(n, m);
+
+ cl_assert(reftable_log_record_equal(&in[i], &out.u.log,
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_log_record_release(&in[i]);
+ reftable_buf_release(&key);
+ reftable_record_release(&out);
+ }
+
+ reftable_buf_release(&scratch);
+}
+
+void test_reftable_record__key_roundtrip(void)
+{
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ struct reftable_buf last_key = REFTABLE_BUF_INIT;
+ struct reftable_buf key = REFTABLE_BUF_INIT;
+ struct reftable_buf roundtrip = REFTABLE_BUF_INIT;
+ int restart;
+ uint8_t extra;
+ int n, m;
+ uint8_t rt_extra;
+
+ cl_assert(reftable_buf_addstr(&last_key, "refs/heads/master") == 0);
+ cl_assert(reftable_buf_addstr(&key, "refs/tags/bla") == 0);
+ extra = 6;
+ n = reftable_encode_key(&restart, dest, last_key, key, extra);
+ cl_assert(restart == 0);
+ cl_assert(n > 0);
+
+ cl_assert(reftable_buf_addstr(&roundtrip, "refs/heads/master") == 0);
+ m = reftable_decode_key(&roundtrip, &rt_extra, dest);
+ cl_assert_equal_i(n, m);
+ cl_assert(reftable_buf_cmp(&key, &roundtrip) == 0);
+ cl_assert_equal_i(rt_extra, extra);
+
+ reftable_buf_release(&last_key);
+ reftable_buf_release(&key);
+ reftable_buf_release(&roundtrip);
+}
+
+void test_reftable_record__obj_record_comparison(void)
+{
+
+ uint8_t id_bytes[] = { 0, 1, 2, 3, 4, 5, 6 };
+ uint64_t offsets[] = { 0, 16, 32, 48, 64, 80, 96, 112};
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_OBJ,
+ .u.obj.hash_prefix = id_bytes,
+ .u.obj.hash_prefix_len = 7,
+ .u.obj.offsets = offsets,
+ .u.obj.offset_len = 8,
+ },
+ {
+ .type = BLOCK_TYPE_OBJ,
+ .u.obj.hash_prefix = id_bytes,
+ .u.obj.hash_prefix_len = 7,
+ .u.obj.offsets = offsets,
+ .u.obj.offset_len = 5,
+ },
+ {
+ .type = BLOCK_TYPE_OBJ,
+ .u.obj.hash_prefix = id_bytes,
+ .u.obj.hash_prefix_len = 5,
+ },
+ };
+ int cmp;
+
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp == 0);
+
+ cl_assert(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[1], &in[2], &cmp) == 0);
+ cl_assert(cmp > 0);
+
+ in[1].u.obj.offset_len = in[0].u.obj.offset_len;
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp == 0);
+}
+
+void test_reftable_record__obj_record_roundtrip(void)
+{
+ uint8_t testHash1[REFTABLE_HASH_SIZE_SHA1] = { 1, 2, 3, 4, 0 };
+ uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
+ struct reftable_obj_record recs[3] = {
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ .offsets = till9,
+ .offset_len = 3,
+ },
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ .offsets = till9,
+ .offset_len = 9,
+ },
+ {
+ .hash_prefix = testHash1,
+ .hash_prefix_len = 5,
+ },
+ };
+ struct reftable_buf scratch = REFTABLE_BUF_INIT;
+
+ for (size_t i = 0; i < ARRAY_SIZE(recs); i++) {
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ struct reftable_record in = {
+ .type = BLOCK_TYPE_OBJ,
+ .u = {
+ .obj = recs[i],
+ },
+ };
+ struct reftable_buf key = REFTABLE_BUF_INIT;
+ struct reftable_record out = { .type = BLOCK_TYPE_OBJ };
+ int n, m;
+ uint8_t extra;
+
+ cl_assert(reftable_record_is_deletion(&in) == 0);
+ t_copy(&in);
+ reftable_record_key(&in, &key);
+ n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
+ cl_assert(n > 0);
+ extra = reftable_record_val_type(&in);
+ m = reftable_record_decode(&out, key, extra, dest,
+ REFTABLE_HASH_SIZE_SHA1, &scratch);
+ cl_assert_equal_i(n, m);
+
+ cl_assert(reftable_record_equal(&in, &out, REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_buf_release(&key);
+ reftable_record_release(&out);
+ }
+
+ reftable_buf_release(&scratch);
+}
+
+void test_reftable_record__index_record_comparison(void)
+{
+ struct reftable_record in[3] = {
+ {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.offset = 22,
+ .u.idx.last_key = REFTABLE_BUF_INIT,
+ },
+ {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.offset = 32,
+ .u.idx.last_key = REFTABLE_BUF_INIT,
+ },
+ {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx.offset = 32,
+ .u.idx.last_key = REFTABLE_BUF_INIT,
+ },
+ };
+ int cmp;
+
+ cl_assert(reftable_buf_addstr(&in[0].u.idx.last_key, "refs/heads/master") == 0);
+ cl_assert(reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master") == 0);
+ cl_assert(reftable_buf_addstr(&in[2].u.idx.last_key, "refs/heads/branch") == 0);
+
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp == 0);
+
+ cl_assert(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1) == 0);
+ cl_assert(reftable_record_cmp(&in[1], &in[2], &cmp) == 0);
+ cl_assert(cmp > 0);
+
+ in[1].u.idx.offset = in[0].u.idx.offset;
+ cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);
+ cl_assert(reftable_record_cmp(&in[0], &in[1], &cmp) == 0);
+ cl_assert(cmp == 0);
+
+ for (size_t i = 0; i < ARRAY_SIZE(in); i++)
+ reftable_record_release(&in[i]);
+}
+
+void test_reftable_record__index_record_roundtrip(void)
+{
+ struct reftable_record in = {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx = {
+ .offset = 42,
+ .last_key = REFTABLE_BUF_INIT,
+ },
+ };
+ uint8_t buffer[1024] = { 0 };
+ struct string_view dest = {
+ .buf = buffer,
+ .len = sizeof(buffer),
+ };
+ struct reftable_buf scratch = REFTABLE_BUF_INIT;
+ struct reftable_buf key = REFTABLE_BUF_INIT;
+ struct reftable_record out = {
+ .type = BLOCK_TYPE_INDEX,
+ .u.idx = { .last_key = REFTABLE_BUF_INIT },
+ };
+ int n, m;
+ uint8_t extra;
+
+ cl_assert(reftable_buf_addstr(&in.u.idx.last_key, "refs/heads/master") == 0);
+ reftable_record_key(&in, &key);
+ t_copy(&in);
+
+ cl_assert(reftable_record_is_deletion(&in) == 0);
+ cl_assert(reftable_buf_cmp(&key, &in.u.idx.last_key) == 0);
+ n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);
+ cl_assert(n > 0);
+
+ extra = reftable_record_val_type(&in);
+ m = reftable_record_decode(&out, key, extra, dest, REFTABLE_HASH_SIZE_SHA1,
+ &scratch);
+ cl_assert_equal_i(m, n);
+
+ cl_assert(reftable_record_equal(&in, &out, REFTABLE_HASH_SIZE_SHA1) != 0);
+
+ reftable_record_release(&out);
+ reftable_buf_release(&key);
+ reftable_buf_release(&scratch);
+ reftable_buf_release(&in.u.idx.last_key);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [PATCH v2 09/10] t/unit-tests: convert reftable stack test to use clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (7 preceding siblings ...)
2025-04-29 17:53 ` [PATCH v2 08/10] t/unit-tests: convert reftable record " Seyi Kuforiji
@ 2025-04-29 17:53 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
2025-04-29 17:53 ` [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar Seyi Kuforiji
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:53 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Adapt reftable stack test file to use clar by using clar assertions
where necessary.
This marks the end of all unit tests migrated away from the
`unit-tests/t-*.c` pattern, there are no longer any files matching that
glob. Remove the sanity check for `t-*.c` files to prevent Meson
configuration errors during CI and local builds.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 2 +-
t/meson.build | 3 +-
t/unit-tests/t-reftable-stack.c | 1451 -------------------------------
t/unit-tests/u-reftable-stack.c | 1247 ++++++++++++++++++++++++++
4 files changed, 1249 insertions(+), 1454 deletions(-)
delete mode 100644 t/unit-tests/t-reftable-stack.c
create mode 100644 t/unit-tests/u-reftable-stack.c
diff --git a/Makefile b/Makefile
index cd9db9adf1..0b42893611 100644
--- a/Makefile
+++ b/Makefile
@@ -1369,6 +1369,7 @@ CLAR_TEST_SUITES += u-reftable-pq
CLAR_TEST_SUITES += u-reftable-reader
CLAR_TEST_SUITES += u-reftable-readwrite
CLAR_TEST_SUITES += u-reftable-record
+CLAR_TEST_SUITES += u-reftable-stack
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
CLAR_TEST_SUITES += u-strcmp-offset
@@ -1381,7 +1382,6 @@ CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-reftable-stack
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
diff --git a/t/meson.build b/t/meson.build
index 756cb2a2dd..8fa00fc9ef 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -15,6 +15,7 @@ clar_test_suites = [
'unit-tests/u-reftable-reader.c',
'unit-tests/u-reftable-readwrite.c',
'unit-tests/u-reftable-record.c',
+ 'unit-tests/u-reftable-stack.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
'unit-tests/u-strcmp-offset.c',
@@ -61,7 +62,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-reftable-stack.c',
]
foreach unit_test_program : unit_test_programs
@@ -1102,7 +1102,6 @@ integration_tests = [
# sufficient to catch missing test suites in our CI though.
foreach glob, tests : {
't[0-9][0-9][0-9][0-9]-*.sh': integration_tests,
- 'unit-tests/t-*.c': unit_test_programs,
'unit-tests/u-*.c': clar_test_suites,
}
actual_tests = run_command(shell, '-c', 'ls ' + glob,
diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/t-reftable-stack.c
deleted file mode 100644
index c3f0059c34..0000000000
--- a/t/unit-tests/t-reftable-stack.c
+++ /dev/null
@@ -1,1451 +0,0 @@
-/*
-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
-*/
-
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
-#include "test-lib.h"
-#include "lib-reftable.h"
-#include "dir.h"
-#include "reftable/merged.h"
-#include "reftable/reader.h"
-#include "reftable/reftable-error.h"
-#include "reftable/stack.h"
-#include "strbuf.h"
-#include "tempfile.h"
-#include <dirent.h>
-
-static void clear_dir(const char *dirname)
-{
- struct strbuf path = REFTABLE_BUF_INIT;
- strbuf_addstr(&path, dirname);
- remove_dir_recursively(&path, 0);
- strbuf_release(&path);
-}
-
-static int count_dir_entries(const char *dirname)
-{
- DIR *dir = opendir(dirname);
- int len = 0;
- struct dirent *d;
- if (!dir)
- return 0;
-
- while ((d = readdir(dir))) {
- /*
- * Besides skipping over "." and "..", we also need to
- * skip over other files that have a leading ".". This
- * is due to behaviour of NFS, which will rename files
- * to ".nfs*" to emulate delete-on-last-close.
- *
- * In any case this should be fine as the reftable
- * library will never write files with leading dots
- * anyway.
- */
- if (starts_with(d->d_name, "."))
- continue;
- len++;
- }
- closedir(dir);
- return len;
-}
-
-/*
- * Work linenumber into the tempdir, so we can see which tests forget to
- * cleanup.
- */
-static char *get_tmp_template(int linenumber)
-{
- const char *tmp = getenv("TMPDIR");
- static char template[1024];
- snprintf(template, sizeof(template) - 1, "%s/stack_test-%d.XXXXXX",
- tmp ? tmp : "/tmp", linenumber);
- return template;
-}
-
-static char *get_tmp_dir(int linenumber)
-{
- char *dir = get_tmp_template(linenumber);
- check(mkdtemp(dir) != NULL);
- return dir;
-}
-
-static void t_read_file(void)
-{
- char *fn = get_tmp_template(__LINE__);
- struct tempfile *tmp = mks_tempfile(fn);
- int fd = get_tempfile_fd(tmp);
- char out[1024] = "line1\n\nline2\nline3";
- int n, err;
- char **names = NULL;
- const char *want[] = { "line1", "line2", "line3" };
-
- check_int(fd, >, 0);
- n = write_in_full(fd, out, strlen(out));
- check_int(n, ==, strlen(out));
- err = close(fd);
- check_int(err, >=, 0);
-
- err = read_lines(fn, &names);
- check(!err);
-
- for (size_t i = 0; names[i]; i++)
- check_str(want[i], names[i]);
- free_names(names);
- (void) remove(fn);
- delete_tempfile(&tmp);
-}
-
-static int write_test_ref(struct reftable_writer *wr, void *arg)
-{
- struct reftable_ref_record *ref = arg;
- check(!reftable_writer_set_limits(wr, ref->update_index,
- ref->update_index));
- return reftable_writer_add_ref(wr, ref);
-}
-
-static void write_n_ref_tables(struct reftable_stack *st,
- size_t n)
-{
- int disable_auto_compact;
- int err;
-
- disable_auto_compact = st->opts.disable_auto_compact;
- st->opts.disable_auto_compact = 1;
-
- for (size_t i = 0; i < n; i++) {
- struct reftable_ref_record ref = {
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_VAL1,
- };
- char buf[128];
-
- snprintf(buf, sizeof(buf), "refs/heads/branch-%04"PRIuMAX, (uintmax_t)i);
- ref.refname = buf;
- t_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- check(!err);
- }
-
- st->opts.disable_auto_compact = disable_auto_compact;
-}
-
-struct write_log_arg {
- struct reftable_log_record *log;
- uint64_t update_index;
-};
-
-static int write_test_log(struct reftable_writer *wr, void *arg)
-{
- struct write_log_arg *wla = arg;
-
- check(!reftable_writer_set_limits(wr, wla->update_index,
- wla->update_index));
- return reftable_writer_add_log(wr, wla->log);
-}
-
-static void t_reftable_stack_add_one(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_buf scratch = REFTABLE_BUF_INIT;
- int mask = umask(002);
- struct reftable_write_options opts = {
- .default_permissions = 0660,
- };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_ref_record ref = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record dest = { 0 };
- struct stat stat_result = { 0 };
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- err = reftable_stack_add(st, write_test_ref, &ref);
- check(!err);
-
- err = reftable_stack_read_ref(st, ref.refname, &dest);
- check(!err);
- check(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1));
- check_int(st->readers_len, >, 0);
-
-#ifndef GIT_WINDOWS_NATIVE
- check(!reftable_buf_addstr(&scratch, dir));
- check(!reftable_buf_addstr(&scratch, "/tables.list"));
- err = stat(scratch.buf, &stat_result);
- check(!err);
- check_int((stat_result.st_mode & 0777), ==, opts.default_permissions);
-
- reftable_buf_reset(&scratch);
- check(!reftable_buf_addstr(&scratch, dir));
- check(!reftable_buf_addstr(&scratch, "/"));
- /* do not try at home; not an external API for reftable. */
- check(!reftable_buf_addstr(&scratch, st->readers[0]->name));
- err = stat(scratch.buf, &stat_result);
- check(!err);
- check_int((stat_result.st_mode & 0777), ==, opts.default_permissions);
-#else
- (void) stat_result;
-#endif
-
- reftable_ref_record_release(&dest);
- reftable_stack_destroy(st);
- reftable_buf_release(&scratch);
- clear_dir(dir);
- umask(mask);
-}
-
-static void t_reftable_stack_uptodate(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL;
- struct reftable_stack *st2 = NULL;
- char *dir = get_tmp_dir(__LINE__);
-
- int err;
- struct reftable_ref_record ref1 = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record ref2 = {
- .refname = (char *) "branch2",
- .update_index = 2,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
-
-
- /* simulate multi-process access to the same stack
- by creating two stacks for the same directory.
- */
- err = reftable_new_stack(&st1, dir, &opts);
- check(!err);
-
- err = reftable_new_stack(&st2, dir, &opts);
- check(!err);
-
- err = reftable_stack_add(st1, write_test_ref, &ref1);
- check(!err);
-
- err = reftable_stack_add(st2, write_test_ref, &ref2);
- check_int(err, ==, REFTABLE_OUTDATED_ERROR);
-
- err = reftable_stack_reload(st2);
- check(!err);
-
- err = reftable_stack_add(st2, write_test_ref, &ref2);
- check(!err);
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_transaction_api(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_addition *add = NULL;
-
- struct reftable_ref_record ref = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record dest = { 0 };
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- reftable_addition_destroy(add);
-
- err = reftable_stack_new_addition(&add, st, 0);
- check(!err);
-
- err = reftable_addition_add(add, write_test_ref, &ref);
- check(!err);
-
- err = reftable_addition_commit(add);
- check(!err);
-
- reftable_addition_destroy(add);
-
- err = reftable_stack_read_ref(st, ref.refname, &dest);
- check(!err);
- check_int(REFTABLE_REF_SYMREF, ==, dest.value_type);
- check(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1));
-
- reftable_ref_record_release(&dest);
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_transaction_with_reload(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_stack *st1 = NULL, *st2 = NULL;
- int err;
- struct reftable_addition *add = NULL;
- struct reftable_ref_record refs[2] = {
- {
- .refname = (char *) "refs/heads/a",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { '1' },
- },
- {
- .refname = (char *) "refs/heads/b",
- .update_index = 2,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = { '1' },
- },
- };
- struct reftable_ref_record ref = { 0 };
-
- err = reftable_new_stack(&st1, dir, NULL);
- check(!err);
- err = reftable_new_stack(&st2, dir, NULL);
- check(!err);
-
- err = reftable_stack_new_addition(&add, st1, 0);
- check(!err);
- err = reftable_addition_add(add, write_test_ref, &refs[0]);
- check(!err);
- err = reftable_addition_commit(add);
- check(!err);
- reftable_addition_destroy(add);
-
- /*
- * The second stack is now outdated, which we should notice. We do not
- * create the addition and lock the stack by default, but allow the
- * reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set.
- */
- err = reftable_stack_new_addition(&add, st2, 0);
- check_int(err, ==, REFTABLE_OUTDATED_ERROR);
- err = reftable_stack_new_addition(&add, st2, REFTABLE_STACK_NEW_ADDITION_RELOAD);
- check(!err);
- err = reftable_addition_add(add, write_test_ref, &refs[1]);
- check(!err);
- err = reftable_addition_commit(add);
- check(!err);
- reftable_addition_destroy(add);
-
- for (size_t i = 0; i < ARRAY_SIZE(refs); i++) {
- err = reftable_stack_read_ref(st2, refs[i].refname, &ref);
- check(!err);
- check(reftable_ref_record_equal(&refs[i], &ref, REFTABLE_HASH_SIZE_SHA1));
- }
-
- reftable_ref_record_release(&ref);
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_transaction_api_performs_auto_compaction(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = {0};
- struct reftable_addition *add = NULL;
- struct reftable_stack *st = NULL;
- size_t n = 20;
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (size_t i = 0; i <= n; i++) {
- struct reftable_ref_record ref = {
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- char name[100];
-
- snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
- ref.refname = name;
-
- /*
- * Disable auto-compaction for all but the last runs. Like this
- * we can ensure that we indeed honor this setting and have
- * better control over when exactly auto compaction runs.
- */
- st->opts.disable_auto_compact = i != n;
-
- err = reftable_stack_new_addition(&add, st, 0);
- check(!err);
-
- err = reftable_addition_add(add, write_test_ref, &ref);
- check(!err);
-
- err = reftable_addition_commit(add);
- check(!err);
-
- reftable_addition_destroy(add);
-
- /*
- * The stack length should grow continuously for all runs where
- * auto compaction is disabled. When enabled, we should merge
- * all tables in the stack.
- */
- if (i != n)
- check_int(st->merged->readers_len, ==, i + 1);
- else
- check_int(st->merged->readers_len, ==, 1);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_auto_compaction_fails_gracefully(void)
-{
- struct reftable_ref_record ref = {
- .refname = (char *) "refs/heads/master",
- .update_index = 1,
- .value_type = REFTABLE_REF_VAL1,
- .value.val1 = {0x01},
- };
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st;
- struct reftable_buf table_path = REFTABLE_BUF_INIT;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- err = reftable_stack_add(st, write_test_ref, &ref);
- check(!err);
- check_int(st->merged->readers_len, ==, 1);
- check_int(st->stats.attempts, ==, 0);
- check_int(st->stats.failures, ==, 0);
-
- /*
- * Lock the newly written table such that it cannot be compacted.
- * Adding a new table to the stack should not be impacted by this, even
- * though auto-compaction will now fail.
- */
- check(!reftable_buf_addstr(&table_path, dir));
- check(!reftable_buf_addstr(&table_path, "/"));
- check(!reftable_buf_addstr(&table_path, st->readers[0]->name));
- check(!reftable_buf_addstr(&table_path, ".lock"));
- write_file_buf(table_path.buf, "", 0);
-
- ref.update_index = 2;
- err = reftable_stack_add(st, write_test_ref, &ref);
- check(!err);
- check_int(st->merged->readers_len, ==, 2);
- check_int(st->stats.attempts, ==, 1);
- check_int(st->stats.failures, ==, 1);
-
- reftable_stack_destroy(st);
- reftable_buf_release(&table_path);
- clear_dir(dir);
-}
-
-static int write_error(struct reftable_writer *wr UNUSED, void *arg)
-{
- return *((int *)arg);
-}
-
-static void t_reftable_stack_update_index_check(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_ref_record ref1 = {
- .refname = (char *) "name1",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_ref_record ref2 = {
- .refname = (char *) "name2",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- err = reftable_stack_add(st, write_test_ref, &ref1);
- check(!err);
-
- err = reftable_stack_add(st, write_test_ref, &ref2);
- check_int(err, ==, REFTABLE_API_ERROR);
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_lock_failure(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err, i;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
- for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
- err = reftable_stack_add(st, write_error, &i);
- check_int(err, ==, i);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_add(void)
-{
- int err = 0;
- struct reftable_write_options opts = {
- .exact_log_message = 1,
- .default_permissions = 0660,
- .disable_auto_compact = 1,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_ref_record refs[2] = { 0 };
- struct reftable_log_record logs[2] = { 0 };
- struct reftable_buf path = REFTABLE_BUF_INIT;
- struct stat stat_result;
- size_t i, N = ARRAY_SIZE(refs);
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (i = 0; i < N; i++) {
- char buf[256];
- snprintf(buf, sizeof(buf), "branch%02"PRIuMAX, (uintmax_t)i);
- refs[i].refname = xstrdup(buf);
- refs[i].update_index = i + 1;
- refs[i].value_type = REFTABLE_REF_VAL1;
- t_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1);
-
- logs[i].refname = xstrdup(buf);
- logs[i].update_index = N + i + 1;
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- logs[i].value.update.email = xstrdup("identity@invalid");
- t_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1);
- }
-
- for (i = 0; i < N; i++) {
- int err = reftable_stack_add(st, write_test_ref, &refs[i]);
- check(!err);
- }
-
- for (i = 0; i < N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
- int err = reftable_stack_add(st, write_test_log, &arg);
- check(!err);
- }
-
- err = reftable_stack_compact_all(st, NULL);
- check(!err);
-
- for (i = 0; i < N; i++) {
- struct reftable_ref_record dest = { 0 };
-
- int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
- check(!err);
- check(reftable_ref_record_equal(&dest, refs + i,
- REFTABLE_HASH_SIZE_SHA1));
- reftable_ref_record_release(&dest);
- }
-
- for (i = 0; i < N; i++) {
- struct reftable_log_record dest = { 0 };
- int err = reftable_stack_read_log(st, refs[i].refname, &dest);
- check(!err);
- check(reftable_log_record_equal(&dest, logs + i,
- REFTABLE_HASH_SIZE_SHA1));
- reftable_log_record_release(&dest);
- }
-
-#ifndef GIT_WINDOWS_NATIVE
- check(!reftable_buf_addstr(&path, dir));
- check(!reftable_buf_addstr(&path, "/tables.list"));
- err = stat(path.buf, &stat_result);
- check(!err);
- check_int((stat_result.st_mode & 0777), ==, opts.default_permissions);
-
- reftable_buf_reset(&path);
- check(!reftable_buf_addstr(&path, dir));
- check(!reftable_buf_addstr(&path, "/"));
- /* do not try at home; not an external API for reftable. */
- check(!reftable_buf_addstr(&path, st->readers[0]->name));
- err = stat(path.buf, &stat_result);
- check(!err);
- check_int((stat_result.st_mode & 0777), ==, opts.default_permissions);
-#else
- (void) stat_result;
-#endif
-
- /* cleanup */
- reftable_stack_destroy(st);
- for (i = 0; i < N; i++) {
- reftable_ref_record_release(&refs[i]);
- reftable_log_record_release(&logs[i]);
- }
- reftable_buf_release(&path);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_iterator(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_ref_record refs[10] = { 0 };
- struct reftable_log_record logs[10] = { 0 };
- struct reftable_iterator it = { 0 };
- size_t N = ARRAY_SIZE(refs), i;
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (i = 0; i < N; i++) {
- refs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
- refs[i].update_index = i + 1;
- refs[i].value_type = REFTABLE_REF_VAL1;
- t_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1);
-
- logs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
- logs[i].update_index = i + 1;
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- logs[i].value.update.email = xstrdup("johndoe@invalid");
- logs[i].value.update.message = xstrdup("commit\n");
- t_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1);
- }
-
- for (i = 0; i < N; i++) {
- err = reftable_stack_add(st, write_test_ref, &refs[i]);
- check(!err);
- }
-
- for (i = 0; i < N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
-
- err = reftable_stack_add(st, write_test_log, &arg);
- check(!err);
- }
-
- reftable_stack_init_ref_iterator(st, &it);
- reftable_iterator_seek_ref(&it, refs[0].refname);
- for (i = 0; ; i++) {
- struct reftable_ref_record ref = { 0 };
-
- err = reftable_iterator_next_ref(&it, &ref);
- if (err > 0)
- break;
- check(!err);
- check(reftable_ref_record_equal(&ref, &refs[i], REFTABLE_HASH_SIZE_SHA1));
- reftable_ref_record_release(&ref);
- }
- check_int(i, ==, N);
-
- reftable_iterator_destroy(&it);
-
- err = reftable_stack_init_log_iterator(st, &it);
- check(!err);
-
- reftable_iterator_seek_log(&it, logs[0].refname);
- for (i = 0; ; i++) {
- struct reftable_log_record log = { 0 };
-
- err = reftable_iterator_next_log(&it, &log);
- if (err > 0)
- break;
- check(!err);
- check(reftable_log_record_equal(&log, &logs[i], REFTABLE_HASH_SIZE_SHA1));
- reftable_log_record_release(&log);
- }
- check_int(i, ==, N);
-
- reftable_stack_destroy(st);
- reftable_iterator_destroy(&it);
- for (i = 0; i < N; i++) {
- reftable_ref_record_release(&refs[i]);
- reftable_log_record_release(&logs[i]);
- }
- clear_dir(dir);
-}
-
-static void t_reftable_stack_log_normalize(void)
-{
- int err = 0;
- struct reftable_write_options opts = {
- 0,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_log_record input = {
- .refname = (char *) "branch",
- .update_index = 1,
- .value_type = REFTABLE_LOG_UPDATE,
- .value = {
- .update = {
- .new_hash = { 1 },
- .old_hash = { 2 },
- },
- },
- };
- struct reftable_log_record dest = {
- .update_index = 0,
- };
- struct write_log_arg arg = {
- .log = &input,
- .update_index = 1,
- };
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- input.value.update.message = (char *) "one\ntwo";
- err = reftable_stack_add(st, write_test_log, &arg);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- input.value.update.message = (char *) "one";
- err = reftable_stack_add(st, write_test_log, &arg);
- check(!err);
-
- err = reftable_stack_read_log(st, input.refname, &dest);
- check(!err);
- check_str(dest.value.update.message, "one\n");
-
- input.value.update.message = (char *) "two\n";
- arg.update_index = 2;
- err = reftable_stack_add(st, write_test_log, &arg);
- check(!err);
- err = reftable_stack_read_log(st, input.refname, &dest);
- check(!err);
- check_str(dest.value.update.message, "two\n");
-
- /* cleanup */
- reftable_stack_destroy(st);
- reftable_log_record_release(&dest);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_tombstone(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- struct reftable_ref_record refs[2] = { 0 };
- struct reftable_log_record logs[2] = { 0 };
- size_t i, N = ARRAY_SIZE(refs);
- struct reftable_ref_record dest = { 0 };
- struct reftable_log_record log_dest = { 0 };
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- /* even entries add the refs, odd entries delete them. */
- for (i = 0; i < N; i++) {
- const char *buf = "branch";
- refs[i].refname = xstrdup(buf);
- refs[i].update_index = i + 1;
- if (i % 2 == 0) {
- refs[i].value_type = REFTABLE_REF_VAL1;
- t_reftable_set_hash(refs[i].value.val1, i,
- REFTABLE_HASH_SHA1);
- }
-
- logs[i].refname = xstrdup(buf);
- /*
- * update_index is part of the key so should be constant.
- * The value itself should be less than the writer's upper
- * limit.
- */
- logs[i].update_index = 1;
- if (i % 2 == 0) {
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- t_reftable_set_hash(logs[i].value.update.new_hash, i,
- REFTABLE_HASH_SHA1);
- logs[i].value.update.email =
- xstrdup("identity@invalid");
- }
- }
- for (i = 0; i < N; i++) {
- int err = reftable_stack_add(st, write_test_ref, &refs[i]);
- check(!err);
- }
-
- for (i = 0; i < N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
- int err = reftable_stack_add(st, write_test_log, &arg);
- check(!err);
- }
-
- err = reftable_stack_read_ref(st, "branch", &dest);
- check_int(err, ==, 1);
- reftable_ref_record_release(&dest);
-
- err = reftable_stack_read_log(st, "branch", &log_dest);
- check_int(err, ==, 1);
- reftable_log_record_release(&log_dest);
-
- err = reftable_stack_compact_all(st, NULL);
- check(!err);
-
- err = reftable_stack_read_ref(st, "branch", &dest);
- check_int(err, ==, 1);
-
- err = reftable_stack_read_log(st, "branch", &log_dest);
- check_int(err, ==, 1);
- reftable_ref_record_release(&dest);
- reftable_log_record_release(&log_dest);
-
- /* cleanup */
- reftable_stack_destroy(st);
- for (i = 0; i < N; i++) {
- reftable_ref_record_release(&refs[i]);
- reftable_log_record_release(&logs[i]);
- }
- clear_dir(dir);
-}
-
-static void t_reftable_stack_hash_id(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
-
- struct reftable_ref_record ref = {
- .refname = (char *) "master",
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "target",
- .update_index = 1,
- };
- struct reftable_write_options opts32 = { .hash_id = REFTABLE_HASH_SHA256 };
- struct reftable_stack *st32 = NULL;
- struct reftable_write_options opts_default = { 0 };
- struct reftable_stack *st_default = NULL;
- struct reftable_ref_record dest = { 0 };
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- err = reftable_stack_add(st, write_test_ref, &ref);
- check(!err);
-
- /* can't read it with the wrong hash ID. */
- err = reftable_new_stack(&st32, dir, &opts32);
- check_int(err, ==, REFTABLE_FORMAT_ERROR);
-
- /* check that we can read it back with default opts too. */
- err = reftable_new_stack(&st_default, dir, &opts_default);
- check(!err);
-
- err = reftable_stack_read_ref(st_default, "master", &dest);
- check(!err);
-
- check(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1));
- reftable_ref_record_release(&dest);
- reftable_stack_destroy(st);
- reftable_stack_destroy(st_default);
- clear_dir(dir);
-}
-
-static void t_suggest_compaction_segment(void)
-{
- uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
- struct segment min =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
- check_int(min.start, ==, 1);
- check_int(min.end, ==, 10);
-}
-
-static void t_suggest_compaction_segment_nothing(void)
-{
- uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
- struct segment result =
- suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
- check_int(result.start, ==, result.end);
-}
-
-static void t_reflog_expire(void)
-{
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- struct reftable_log_record logs[20] = { 0 };
- size_t i, N = ARRAY_SIZE(logs) - 1;
- int err;
- struct reftable_log_expiry_config expiry = {
- .time = 10,
- };
- struct reftable_log_record log = { 0 };
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (i = 1; i <= N; i++) {
- char buf[256];
- snprintf(buf, sizeof(buf), "branch%02"PRIuMAX, (uintmax_t)i);
-
- logs[i].refname = xstrdup(buf);
- logs[i].update_index = i;
- logs[i].value_type = REFTABLE_LOG_UPDATE;
- logs[i].value.update.time = i;
- logs[i].value.update.email = xstrdup("identity@invalid");
- t_reftable_set_hash(logs[i].value.update.new_hash, i,
- REFTABLE_HASH_SHA1);
- }
-
- for (i = 1; i <= N; i++) {
- struct write_log_arg arg = {
- .log = &logs[i],
- .update_index = reftable_stack_next_update_index(st),
- };
- int err = reftable_stack_add(st, write_test_log, &arg);
- check(!err);
- }
-
- err = reftable_stack_compact_all(st, NULL);
- check(!err);
-
- err = reftable_stack_compact_all(st, &expiry);
- check(!err);
-
- err = reftable_stack_read_log(st, logs[9].refname, &log);
- check_int(err, ==, 1);
-
- err = reftable_stack_read_log(st, logs[11].refname, &log);
- check(!err);
-
- expiry.min_update_index = 15;
- err = reftable_stack_compact_all(st, &expiry);
- check(!err);
-
- err = reftable_stack_read_log(st, logs[14].refname, &log);
- check_int(err, ==, 1);
-
- err = reftable_stack_read_log(st, logs[16].refname, &log);
- check(!err);
-
- /* cleanup */
- reftable_stack_destroy(st);
- for (i = 0; i <= N; i++)
- reftable_log_record_release(&logs[i]);
- clear_dir(dir);
- reftable_log_record_release(&log);
-}
-
-static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)
-{
- check(!reftable_writer_set_limits(wr, 1, 1));
- return 0;
-}
-
-static void t_empty_add(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- int err;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_stack *st2 = NULL;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- err = reftable_stack_add(st, write_nothing, NULL);
- check(!err);
-
- err = reftable_new_stack(&st2, dir, &opts);
- check(!err);
- clear_dir(dir);
- reftable_stack_destroy(st);
- reftable_stack_destroy(st2);
-}
-
-static int fastlogN(uint64_t sz, uint64_t N)
-{
- int l = 0;
- if (sz == 0)
- return 0;
- for (; sz; sz /= N)
- l++;
- return l - 1;
-}
-
-static void t_reftable_stack_auto_compaction(void)
-{
- struct reftable_write_options opts = {
- .disable_auto_compact = 1,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err;
- size_t i, N = 100;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (i = 0; i < N; i++) {
- char name[100];
- struct reftable_ref_record ref = {
- .refname = name,
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
-
- err = reftable_stack_add(st, write_test_ref, &ref);
- check(!err);
-
- err = reftable_stack_auto_compact(st);
- check(!err);
- check(i < 2 || st->merged->readers_len < 2 * fastlogN(i, 2));
- }
-
- check_int(reftable_stack_compaction_stats(st)->entries_written, <,
- (uint64_t)(N * fastlogN(N, 2)));
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_auto_compaction_factor(void)
-{
- struct reftable_write_options opts = {
- .auto_compaction_factor = 5,
- };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err;
- size_t N = 100;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (size_t i = 0; i < N; i++) {
- char name[20];
- struct reftable_ref_record ref = {
- .refname = name,
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_VAL1,
- };
- xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
-
- err = reftable_stack_add(st, &write_test_ref, &ref);
- check(!err);
-
- check(i < 5 || st->merged->readers_len < 5 * fastlogN(i, 5));
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_auto_compaction_with_locked_tables(void)
-{
- struct reftable_write_options opts = {
- .disable_auto_compact = 1,
- };
- struct reftable_stack *st = NULL;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- write_n_ref_tables(st, 5);
- check_int(st->merged->readers_len, ==, 5);
-
- /*
- * Given that all tables we have written should be roughly the same
- * size, we expect that auto-compaction will want to compact all of the
- * tables. Locking any of the tables will keep it from doing so.
- */
- check(!reftable_buf_addstr(&buf, dir));
- check(!reftable_buf_addstr(&buf, "/"));
- check(!reftable_buf_addstr(&buf, st->readers[2]->name));
- check(!reftable_buf_addstr(&buf, ".lock"));
- write_file_buf(buf.buf, "", 0);
-
- /*
- * When parts of the stack are locked, then auto-compaction does a best
- * effort compaction of those tables which aren't locked. So while this
- * would in theory compact all tables, due to the preexisting lock we
- * only compact the newest two tables.
- */
- err = reftable_stack_auto_compact(st);
- check(!err);
- check_int(st->stats.failures, ==, 0);
- check_int(st->merged->readers_len, ==, 4);
-
- reftable_stack_destroy(st);
- reftable_buf_release(&buf);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_add_performs_auto_compaction(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err;
- size_t i, n = 20;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- for (i = 0; i <= n; i++) {
- struct reftable_ref_record ref = {
- .update_index = reftable_stack_next_update_index(st),
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- char buf[128];
-
- /*
- * Disable auto-compaction for all but the last runs. Like this
- * we can ensure that we indeed honor this setting and have
- * better control over when exactly auto compaction runs.
- */
- st->opts.disable_auto_compact = i != n;
-
- snprintf(buf, sizeof(buf), "branch-%04"PRIuMAX, (uintmax_t)i);
- ref.refname = buf;
-
- err = reftable_stack_add(st, write_test_ref, &ref);
- check(!err);
-
- /*
- * The stack length should grow continuously for all runs where
- * auto compaction is disabled. When enabled, we should merge
- * all tables in the stack.
- */
- if (i != n)
- check_int(st->merged->readers_len, ==, i + 1);
- else
- check_int(st->merged->readers_len, ==, 1);
- }
-
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_compaction_with_locked_tables(void)
-{
- struct reftable_write_options opts = {
- .disable_auto_compact = 1,
- };
- struct reftable_stack *st = NULL;
- struct reftable_buf buf = REFTABLE_BUF_INIT;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- write_n_ref_tables(st, 3);
- check_int(st->merged->readers_len, ==, 3);
-
- /* Lock one of the tables that we're about to compact. */
- check(!reftable_buf_addstr(&buf, dir));
- check(!reftable_buf_addstr(&buf, "/"));
- check(!reftable_buf_addstr(&buf, st->readers[1]->name));
- check(!reftable_buf_addstr(&buf, ".lock"));
- write_file_buf(buf.buf, "", 0);
-
- /*
- * Compaction is expected to fail given that we were not able to
- * compact all tables.
- */
- err = reftable_stack_compact_all(st, NULL);
- check_int(err, ==, REFTABLE_LOCK_ERROR);
- check_int(st->stats.failures, ==, 1);
- check_int(st->merged->readers_len, ==, 3);
-
- reftable_stack_destroy(st);
- reftable_buf_release(&buf);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_compaction_concurrent(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL, *st2 = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- err = reftable_new_stack(&st1, dir, &opts);
- check(!err);
- write_n_ref_tables(st1, 3);
-
- err = reftable_new_stack(&st2, dir, &opts);
- check(!err);
-
- err = reftable_stack_compact_all(st1, NULL);
- check(!err);
-
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
-
- check_int(count_dir_entries(dir), ==, 2);
- clear_dir(dir);
-}
-
-static void unclean_stack_close(struct reftable_stack *st)
-{
- /* break abstraction boundary to simulate unclean shutdown. */
- for (size_t i = 0; i < st->readers_len; i++)
- reftable_reader_decref(st->readers[i]);
- st->readers_len = 0;
- REFTABLE_FREE_AND_NULL(st->readers);
-}
-
-static void t_reftable_stack_compaction_concurrent_clean(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- err = reftable_new_stack(&st1, dir, &opts);
- check(!err);
- write_n_ref_tables(st1, 3);
-
- err = reftable_new_stack(&st2, dir, &opts);
- check(!err);
-
- err = reftable_stack_compact_all(st1, NULL);
- check(!err);
-
- unclean_stack_close(st1);
- unclean_stack_close(st2);
-
- err = reftable_new_stack(&st3, dir, &opts);
- check(!err);
-
- err = reftable_stack_clean(st3);
- check(!err);
- check_int(count_dir_entries(dir), ==, 2);
-
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
- reftable_stack_destroy(st3);
-
- clear_dir(dir);
-}
-
-static void t_reftable_stack_read_across_reload(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st1 = NULL, *st2 = NULL;
- struct reftable_ref_record rec = { 0 };
- struct reftable_iterator it = { 0 };
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- /* Create a first stack and set up an iterator for it. */
- err = reftable_new_stack(&st1, dir, &opts);
- check(!err);
- write_n_ref_tables(st1, 2);
- check_int(st1->merged->readers_len, ==, 2);
- reftable_stack_init_ref_iterator(st1, &it);
- err = reftable_iterator_seek_ref(&it, "");
- check(!err);
-
- /* Set up a second stack for the same directory and compact it. */
- err = reftable_new_stack(&st2, dir, &opts);
- check(!err);
- check_int(st2->merged->readers_len, ==, 2);
- err = reftable_stack_compact_all(st2, NULL);
- check(!err);
- check_int(st2->merged->readers_len, ==, 1);
-
- /*
- * Verify that we can continue to use the old iterator even after we
- * have reloaded its stack.
- */
- err = reftable_stack_reload(st1);
- check(!err);
- check_int(st1->merged->readers_len, ==, 1);
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- check_str(rec.refname, "refs/heads/branch-0000");
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- check_str(rec.refname, "refs/heads/branch-0001");
- err = reftable_iterator_next_ref(&it, &rec);
- check_int(err, >, 0);
-
- reftable_ref_record_release(&rec);
- reftable_iterator_destroy(&it);
- reftable_stack_destroy(st1);
- reftable_stack_destroy(st2);
- clear_dir(dir);
-}
-
-static void t_reftable_stack_reload_with_missing_table(void)
-{
- struct reftable_write_options opts = { 0 };
- struct reftable_stack *st = NULL;
- struct reftable_ref_record rec = { 0 };
- struct reftable_iterator it = { 0 };
- struct reftable_buf table_path = REFTABLE_BUF_INIT, content = REFTABLE_BUF_INIT;
- char *dir = get_tmp_dir(__LINE__);
- int err;
-
- /* Create a first stack and set up an iterator for it. */
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
- write_n_ref_tables(st, 2);
- check_int(st->merged->readers_len, ==, 2);
- reftable_stack_init_ref_iterator(st, &it);
- err = reftable_iterator_seek_ref(&it, "");
- check(!err);
-
- /*
- * Update the tables.list file with some garbage data, while reusing
- * our old readers. This should trigger a partial reload of the stack,
- * where we try to reuse our old readers.
- */
- check(!reftable_buf_addstr(&content, st->readers[0]->name));
- check(!reftable_buf_addstr(&content, "\n"));
- check(!reftable_buf_addstr(&content, st->readers[1]->name));
- check(!reftable_buf_addstr(&content, "\n"));
- check(!reftable_buf_addstr(&content, "garbage\n"));
- check(!reftable_buf_addstr(&table_path, st->list_file));
- check(!reftable_buf_addstr(&table_path, ".lock"));
- write_file_buf(table_path.buf, content.buf, content.len);
- err = rename(table_path.buf, st->list_file);
- check(!err);
-
- err = reftable_stack_reload(st);
- check_int(err, ==, -4);
- check_int(st->merged->readers_len, ==, 2);
-
- /*
- * Even though the reload has failed, we should be able to continue
- * using the iterator.
- */
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- check_str(rec.refname, "refs/heads/branch-0000");
- err = reftable_iterator_next_ref(&it, &rec);
- check(!err);
- check_str(rec.refname, "refs/heads/branch-0001");
- err = reftable_iterator_next_ref(&it, &rec);
- check_int(err, >, 0);
-
- reftable_ref_record_release(&rec);
- reftable_iterator_destroy(&it);
- reftable_stack_destroy(st);
- reftable_buf_release(&table_path);
- reftable_buf_release(&content);
- clear_dir(dir);
-}
-
-static int write_limits_after_ref(struct reftable_writer *wr, void *arg)
-{
- struct reftable_ref_record *ref = arg;
- check(!reftable_writer_set_limits(wr, ref->update_index, ref->update_index));
- check(!reftable_writer_add_ref(wr, ref));
- return reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
-}
-
-static void t_reftable_invalid_limit_updates(void)
-{
- struct reftable_ref_record ref = {
- .refname = (char *) "HEAD",
- .update_index = 1,
- .value_type = REFTABLE_REF_SYMREF,
- .value.symref = (char *) "master",
- };
- struct reftable_write_options opts = {
- .default_permissions = 0660,
- };
- struct reftable_addition *add = NULL;
- char *dir = get_tmp_dir(__LINE__);
- struct reftable_stack *st = NULL;
- int err;
-
- err = reftable_new_stack(&st, dir, &opts);
- check(!err);
-
- reftable_addition_destroy(add);
-
- err = reftable_stack_new_addition(&add, st, 0);
- check(!err);
-
- /*
- * write_limits_after_ref also updates the update indexes after adding
- * the record. This should cause an err to be returned, since the limits
- * must be set at the start.
- */
- err = reftable_addition_add(add, write_limits_after_ref, &ref);
- check_int(err, ==, REFTABLE_API_ERROR);
-
- reftable_addition_destroy(add);
- reftable_stack_destroy(st);
- clear_dir(dir);
-}
-
-int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
-{
- TEST(t_empty_add(), "empty addition to stack");
- TEST(t_read_file(), "read_lines works");
- TEST(t_reflog_expire(), "expire reflog entries");
- TEST(t_reftable_invalid_limit_updates(), "prevent limit updates after adding records");
- TEST(t_reftable_stack_add(), "add multiple refs and logs to stack");
- TEST(t_reftable_stack_add_one(), "add a single ref record to stack");
- TEST(t_reftable_stack_add_performs_auto_compaction(), "addition to stack triggers auto-compaction");
- TEST(t_reftable_stack_auto_compaction(), "stack must form geometric sequence after compaction");
- TEST(t_reftable_stack_auto_compaction_factor(), "auto-compaction with non-default geometric factor");
- TEST(t_reftable_stack_auto_compaction_fails_gracefully(), "failure on auto-compaction");
- TEST(t_reftable_stack_auto_compaction_with_locked_tables(), "auto compaction with locked tables");
- TEST(t_reftable_stack_compaction_concurrent(), "compaction with concurrent stack");
- TEST(t_reftable_stack_compaction_concurrent_clean(), "compaction with unclean stack shutdown");
- TEST(t_reftable_stack_compaction_with_locked_tables(), "compaction with locked tables");
- TEST(t_reftable_stack_hash_id(), "read stack with wrong hash ID");
- TEST(t_reftable_stack_iterator(), "log and ref iterator for reftable stack");
- TEST(t_reftable_stack_lock_failure(), "stack addition with lockfile failure");
- TEST(t_reftable_stack_log_normalize(), "log messages should be normalized");
- TEST(t_reftable_stack_read_across_reload(), "stack iterators work across reloads");
- TEST(t_reftable_stack_reload_with_missing_table(), "stack iteration with garbage tables");
- TEST(t_reftable_stack_tombstone(), "'tombstone' refs in stack");
- TEST(t_reftable_stack_transaction_api(), "update transaction to stack");
- TEST(t_reftable_stack_transaction_with_reload(), "transaction with reload");
- TEST(t_reftable_stack_transaction_api_performs_auto_compaction(), "update transaction triggers auto-compaction");
- TEST(t_reftable_stack_update_index_check(), "update transactions with equal update indices");
- TEST(t_reftable_stack_uptodate(), "stack must be reloaded before ref update");
- TEST(t_suggest_compaction_segment(), "suggest_compaction_segment with basic input");
- TEST(t_suggest_compaction_segment_nothing(), "suggest_compaction_segment with pre-compacted input");
-
- return test_done();
-}
diff --git a/t/unit-tests/u-reftable-stack.c b/t/unit-tests/u-reftable-stack.c
new file mode 100644
index 0000000000..1f41d8c4ca
--- /dev/null
+++ b/t/unit-tests/u-reftable-stack.c
@@ -0,0 +1,1247 @@
+/*
+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
+*/
+
+#define DISABLE_SIGN_COMPARE_WARNINGS
+
+#include "unit-test.h"
+#include "lib-reftable.h"
+#include "dir.h"
+#include "reftable/merged.h"
+#include "reftable/reader.h"
+#include "reftable/reftable-error.h"
+#include "reftable/stack.h"
+#include "strbuf.h"
+#include "tempfile.h"
+#include <dirent.h>
+
+static void clear_dir(const char *dirname)
+{
+ struct strbuf path = REFTABLE_BUF_INIT;
+ strbuf_addstr(&path, dirname);
+ remove_dir_recursively(&path, 0);
+ strbuf_release(&path);
+}
+
+static int count_dir_entries(const char *dirname)
+{
+ DIR *dir = opendir(dirname);
+ int len = 0;
+ struct dirent *d;
+ if (!dir)
+ return 0;
+
+ while ((d = readdir(dir))) {
+ /*
+ * Besides skipping over "." and "..", we also need to
+ * skip over other files that have a leading ".". This
+ * is due to behaviour of NFS, which will rename files
+ * to ".nfs*" to emulate delete-on-last-close.
+ *
+ * In any case this should be fine as the reftable
+ * library will never write files with leading dots
+ * anyway.
+ */
+ if (starts_with(d->d_name, "."))
+ continue;
+ len++;
+ }
+ closedir(dir);
+ return len;
+}
+
+/*
+ * Work linenumber into the tempdir, so we can see which tests forget to
+ * cleanup.
+ */
+static char *get_tmp_template(int linenumber)
+{
+ const char *tmp = getenv("TMPDIR");
+ static char template[1024];
+ snprintf(template, sizeof(template) - 1, "%s/stack_test-%d.XXXXXX",
+ tmp ? tmp : "/tmp", linenumber);
+ return template;
+}
+
+static char *get_tmp_dir(int linenumber)
+{
+ char *dir = get_tmp_template(linenumber);
+ cl_assert(mkdtemp(dir) != NULL);
+ return dir;
+}
+
+void test_reftable_stack__read_file(void)
+{
+ char *fn = get_tmp_template(__LINE__);
+ struct tempfile *tmp = mks_tempfile(fn);
+ int fd = get_tempfile_fd(tmp);
+ char out[1024] = "line1\n\nline2\nline3";
+ int n, err;
+ char **names = NULL;
+ const char *want[] = { "line1", "line2", "line3" };
+
+ cl_assert(fd > 0);
+ n = write_in_full(fd, out, strlen(out));
+ cl_assert_equal_i(n, strlen(out));
+ err = close(fd);
+ cl_assert(err >= 0);
+
+ err = read_lines(fn, &names);
+ cl_assert(err == 0);
+
+ for (size_t i = 0; names[i]; i++)
+ cl_assert_equal_s(want[i], names[i]);
+ free_names(names);
+ (void) remove(fn);
+ delete_tempfile(&tmp);
+}
+
+static int write_test_ref(struct reftable_writer *wr, void *arg)
+{
+ struct reftable_ref_record *ref = arg;
+ cl_assert(reftable_writer_set_limits(wr, ref->update_index,
+ ref->update_index) == 0);
+ return reftable_writer_add_ref(wr, ref);
+}
+
+static void write_n_ref_tables(struct reftable_stack *st,
+ size_t n)
+{
+ int disable_auto_compact;
+
+ disable_auto_compact = st->opts.disable_auto_compact;
+ st->opts.disable_auto_compact = 1;
+
+ for (size_t i = 0; i < n; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = reftable_stack_next_update_index(st),
+ .value_type = REFTABLE_REF_VAL1,
+ };
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "refs/heads/branch-%04"PRIuMAX, (uintmax_t)i);
+ ref.refname = buf;
+ cl_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1);
+
+ cl_assert(reftable_stack_add(st, &write_test_ref, &ref) == 0);
+ }
+
+ st->opts.disable_auto_compact = disable_auto_compact;
+}
+
+struct write_log_arg {
+ struct reftable_log_record *log;
+ uint64_t update_index;
+};
+
+static int write_test_log(struct reftable_writer *wr, void *arg)
+{
+ struct write_log_arg *wla = arg;
+
+ cl_assert(reftable_writer_set_limits(wr, wla->update_index,
+ wla->update_index) == 0);
+ return reftable_writer_add_log(wr, wla->log);
+}
+
+void test_reftable_stack__add_one(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_buf scratch = REFTABLE_BUF_INIT;
+ int mask = umask(002);
+ struct reftable_write_options opts = {
+ .default_permissions = 0660,
+ };
+ struct reftable_stack *st = NULL;
+ struct reftable_ref_record ref = {
+ .refname = (char *) "HEAD",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ struct reftable_ref_record dest = { 0 };
+ struct stat stat_result = { 0 };
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref) == 0);
+ cl_assert(reftable_stack_read_ref(st, ref.refname, &dest) == 0);
+ cl_assert(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1) != 0);
+ cl_assert(st->readers_len > 0);
+
+#ifndef GIT_WINDOWS_NATIVE
+ cl_assert(reftable_buf_addstr(&scratch, dir) == 0);
+ cl_assert(reftable_buf_addstr(&scratch, "/tables.list") == 0);
+ cl_assert(stat(scratch.buf, &stat_result) == 0);
+ cl_assert_equal_i((stat_result.st_mode & 0777), opts.default_permissions);
+
+ reftable_buf_reset(&scratch);
+ cl_assert(reftable_buf_addstr(&scratch, dir) == 0);
+ cl_assert(reftable_buf_addstr(&scratch, "/") == 0);
+ /* do not try at home; not an external API for reftable. */
+ cl_assert(reftable_buf_addstr(&scratch, st->readers[0]->name) == 0);
+ cl_assert(stat(scratch.buf, &stat_result) == 0);
+ cl_assert_equal_i((stat_result.st_mode & 0777), opts.default_permissions);
+#else
+ (void) stat_result;
+#endif
+
+ reftable_ref_record_release(&dest);
+ reftable_stack_destroy(st);
+ reftable_buf_release(&scratch);
+ clear_dir(dir);
+ umask(mask);
+}
+
+void test_reftable_stack__uptodate(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st1 = NULL;
+ struct reftable_stack *st2 = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+
+ struct reftable_ref_record ref1 = {
+ .refname = (char *) "HEAD",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ struct reftable_ref_record ref2 = {
+ .refname = (char *) "branch2",
+ .update_index = 2,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+
+
+ /* simulate multi-process access to the same stack
+ by creating two stacks for the same directory.
+ */
+ cl_assert(reftable_new_stack(&st1, dir, &opts) == 0);
+ cl_assert(reftable_new_stack(&st2, dir, &opts) == 0);
+ cl_assert(reftable_stack_add(st1, write_test_ref, &ref1) == 0);
+ cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, &ref2),
+ REFTABLE_OUTDATED_ERROR);
+ cl_assert(reftable_stack_reload(st2) == 0);
+ cl_assert(reftable_stack_add(st2, write_test_ref, &ref2) == 0);
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__transaction_api(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ struct reftable_addition *add = NULL;
+
+ struct reftable_ref_record ref = {
+ .refname = (char *) "HEAD",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ struct reftable_ref_record dest = { 0 };
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ reftable_addition_destroy(add);
+
+ cl_assert(reftable_stack_new_addition(&add, st, 0) == 0);
+ cl_assert(reftable_addition_add(add, write_test_ref, &ref) == 0);
+ cl_assert(reftable_addition_commit(add) == 0);
+
+ reftable_addition_destroy(add);
+
+ cl_assert(reftable_stack_read_ref(st, ref.refname, &dest) == 0);
+ cl_assert_equal_i(REFTABLE_REF_SYMREF, dest.value_type);
+ cl_assert(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1) != 0);
+
+ reftable_ref_record_release(&dest);
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__transaction_with_reload(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_stack *st1 = NULL, *st2 = NULL;
+ struct reftable_addition *add = NULL;
+ struct reftable_ref_record refs[2] = {
+ {
+ .refname = (char *) "refs/heads/a",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { '1' },
+ },
+ {
+ .refname = (char *) "refs/heads/b",
+ .update_index = 2,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = { '1' },
+ },
+ };
+ struct reftable_ref_record ref = { 0 };
+
+ cl_assert(reftable_new_stack(&st1, dir, NULL) == 0);
+ cl_assert(reftable_new_stack(&st2, dir, NULL) == 0);
+ cl_assert(reftable_stack_new_addition(&add, st1, 0) == 0);
+ cl_assert(reftable_addition_add(add, write_test_ref, &refs[0]) == 0);
+ cl_assert(reftable_addition_commit(add) == 0);
+ reftable_addition_destroy(add);
+
+ /*
+ * The second stack is now outdated, which we should notice. We do not
+ * create the addition and lock the stack by default, but allow the
+ * reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set.
+ */
+ cl_assert_equal_i(reftable_stack_new_addition(&add, st2, 0),
+ REFTABLE_OUTDATED_ERROR);
+ cl_assert(reftable_stack_new_addition(&add, st2,
+ REFTABLE_STACK_NEW_ADDITION_RELOAD) == 0);
+ cl_assert(reftable_addition_add(add, write_test_ref, &refs[1]) == 0);
+ cl_assert(reftable_addition_commit(add) == 0);
+ reftable_addition_destroy(add);
+
+ for (size_t i = 0; i < ARRAY_SIZE(refs); i++) {
+ cl_assert(reftable_stack_read_ref(st2, refs[i].refname, &ref) == 0);
+ cl_assert(reftable_ref_record_equal(&refs[i], &ref,
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ }
+
+ reftable_ref_record_release(&ref);
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__transaction_api_performs_auto_compaction(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = {0};
+ struct reftable_addition *add = NULL;
+ struct reftable_stack *st = NULL;
+ size_t n = 20;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (size_t i = 0; i <= n; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = reftable_stack_next_update_index(st),
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ char name[100];
+
+ snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
+ ref.refname = name;
+
+ /*
+ * Disable auto-compaction for all but the last runs. Like this
+ * we can ensure that we indeed honor this setting and have
+ * better control over when exactly auto compaction runs.
+ */
+ st->opts.disable_auto_compact = i != n;
+
+ cl_assert(reftable_stack_new_addition(&add, st, 0) == 0);
+ cl_assert(reftable_addition_add(add, write_test_ref, &ref) == 0);
+ cl_assert(reftable_addition_commit(add) == 0);
+
+ reftable_addition_destroy(add);
+
+ /*
+ * The stack length should grow continuously for all runs where
+ * auto compaction is disabled. When enabled, we should merge
+ * all tables in the stack.
+ */
+ if (i != n)
+ cl_assert_equal_i(st->merged->readers_len, i + 1);
+ else
+ cl_assert_equal_i(st->merged->readers_len, 1);
+ }
+
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__auto_compaction_fails_gracefully(void)
+{
+ struct reftable_ref_record ref = {
+ .refname = (char *) "refs/heads/master",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_VAL1,
+ .value.val1 = {0x01},
+ };
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st;
+ struct reftable_buf table_path = REFTABLE_BUF_INIT;
+ char *dir = get_tmp_dir(__LINE__);
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref) == 0);
+ cl_assert_equal_i(st->merged->readers_len, 1);
+ cl_assert_equal_i(st->stats.attempts, 0);
+ cl_assert_equal_i(st->stats.failures, 0);
+
+ /*
+ * Lock the newly written table such that it cannot be compacted.
+ * Adding a new table to the stack should not be impacted by this, even
+ * though auto-compaction will now fail.
+ */
+ cl_assert(reftable_buf_addstr(&table_path, dir) == 0);
+ cl_assert(reftable_buf_addstr(&table_path, "/") == 0);
+ cl_assert(reftable_buf_addstr(&table_path, st->readers[0]->name) == 0);
+ cl_assert(reftable_buf_addstr(&table_path, ".lock") == 0);
+ write_file_buf(table_path.buf, "", 0);
+
+ ref.update_index = 2;
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref) == 0);
+ cl_assert_equal_i(st->merged->readers_len, 2);
+ cl_assert_equal_i(st->stats.attempts, 1);
+ cl_assert_equal_i(st->stats.failures, 1);
+
+ reftable_stack_destroy(st);
+ reftable_buf_release(&table_path);
+ clear_dir(dir);
+}
+
+static int write_error(struct reftable_writer *wr UNUSED, void *arg)
+{
+ return *((int *)arg);
+}
+
+void test_reftable_stack__update_index_check(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ struct reftable_ref_record ref1 = {
+ .refname = (char *) "name1",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ struct reftable_ref_record ref2 = {
+ .refname = (char *) "name2",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref1) == 0);
+ cl_assert_equal_i(reftable_stack_add(st, write_test_ref,
+ &ref2), REFTABLE_API_ERROR);
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__lock_failure(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ int i;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--)
+ cl_assert_equal_i(reftable_stack_add(st, write_error, &i), i);
+
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__add(void)
+{
+ struct reftable_write_options opts = {
+ .exact_log_message = 1,
+ .default_permissions = 0660,
+ .disable_auto_compact = 1,
+ };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_ref_record refs[2] = { 0 };
+ struct reftable_log_record logs[2] = { 0 };
+ struct reftable_buf path = REFTABLE_BUF_INIT;
+ struct stat stat_result;
+ size_t i, N = ARRAY_SIZE(refs);
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (i = 0; i < N; i++) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "branch%02"PRIuMAX, (uintmax_t)i);
+ refs[i].refname = xstrdup(buf);
+ refs[i].update_index = i + 1;
+ refs[i].value_type = REFTABLE_REF_VAL1;
+ cl_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1);
+
+ logs[i].refname = xstrdup(buf);
+ logs[i].update_index = N + i + 1;
+ logs[i].value_type = REFTABLE_LOG_UPDATE;
+ logs[i].value.update.email = xstrdup("identity@invalid");
+ cl_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1);
+ }
+
+ for (i = 0; i < N; i++)
+ cl_assert(reftable_stack_add(st, write_test_ref, &refs[i]) == 0);
+
+ for (i = 0; i < N; i++) {
+ struct write_log_arg arg = {
+ .log = &logs[i],
+ .update_index = reftable_stack_next_update_index(st),
+ };
+ cl_assert(reftable_stack_add(st, write_test_log, &arg) == 0);
+ }
+
+ cl_assert(reftable_stack_compact_all(st, NULL) == 0);
+
+ for (i = 0; i < N; i++) {
+ struct reftable_ref_record dest = { 0 };
+
+ cl_assert(reftable_stack_read_ref(st, refs[i].refname, &dest) == 0);
+ cl_assert(reftable_ref_record_equal(&dest, refs + i,
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_ref_record_release(&dest);
+ }
+
+ for (i = 0; i < N; i++) {
+ struct reftable_log_record dest = { 0 };
+ cl_assert(reftable_stack_read_log(st, refs[i].refname, &dest) == 0);
+ cl_assert(reftable_log_record_equal(&dest, logs + i,
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_log_record_release(&dest);
+ }
+
+#ifndef GIT_WINDOWS_NATIVE
+ cl_assert(reftable_buf_addstr(&path, dir) == 0);
+ cl_assert(reftable_buf_addstr(&path, "/tables.list") == 0);
+ cl_assert(stat(path.buf, &stat_result) == 0);
+ cl_assert_equal_i((stat_result.st_mode & 0777), opts.default_permissions);
+
+ reftable_buf_reset(&path);
+ cl_assert(reftable_buf_addstr(&path, dir) == 0);
+ cl_assert(reftable_buf_addstr(&path, "/") == 0);
+ /* do not try at home; not an external API for reftable. */
+ cl_assert(reftable_buf_addstr(&path, st->readers[0]->name) == 0);
+ cl_assert(stat(path.buf, &stat_result) == 0);
+ cl_assert_equal_i((stat_result.st_mode & 0777), opts.default_permissions);
+#else
+ (void) stat_result;
+#endif
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ for (i = 0; i < N; i++) {
+ reftable_ref_record_release(&refs[i]);
+ reftable_log_record_release(&logs[i]);
+ }
+ reftable_buf_release(&path);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__iterator(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_ref_record refs[10] = { 0 };
+ struct reftable_log_record logs[10] = { 0 };
+ struct reftable_iterator it = { 0 };
+ size_t N = ARRAY_SIZE(refs), i;
+ int err;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (i = 0; i < N; i++) {
+ refs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
+ refs[i].update_index = i + 1;
+ refs[i].value_type = REFTABLE_REF_VAL1;
+ cl_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1);
+
+ logs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i);
+ logs[i].update_index = i + 1;
+ logs[i].value_type = REFTABLE_LOG_UPDATE;
+ logs[i].value.update.email = xstrdup("johndoe@invalid");
+ logs[i].value.update.message = xstrdup("commit\n");
+ cl_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1);
+ }
+
+ for (i = 0; i < N; i++)
+ cl_assert(reftable_stack_add(st, write_test_ref, &refs[i]) == 0);
+
+ for (i = 0; i < N; i++) {
+ struct write_log_arg arg = {
+ .log = &logs[i],
+ .update_index = reftable_stack_next_update_index(st),
+ };
+
+ cl_assert(reftable_stack_add(st, write_test_log, &arg) == 0);
+ }
+
+ reftable_stack_init_ref_iterator(st, &it);
+ reftable_iterator_seek_ref(&it, refs[0].refname);
+ for (i = 0; ; i++) {
+ struct reftable_ref_record ref = { 0 };
+
+ err = reftable_iterator_next_ref(&it, &ref);
+ if (err > 0)
+ break;
+ cl_assert(err == 0);
+ cl_assert(reftable_ref_record_equal(&ref, &refs[i],
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_ref_record_release(&ref);
+ }
+ cl_assert_equal_i(i, N);
+
+ reftable_iterator_destroy(&it);
+
+ cl_assert(reftable_stack_init_log_iterator(st, &it) == 0);
+
+ reftable_iterator_seek_log(&it, logs[0].refname);
+ for (i = 0; ; i++) {
+ struct reftable_log_record log = { 0 };
+
+ err = reftable_iterator_next_log(&it, &log);
+ if (err > 0)
+ break;
+ cl_assert(err == 0);
+ cl_assert(reftable_log_record_equal(&log, &logs[i],
+ REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_log_record_release(&log);
+ }
+ cl_assert_equal_i(i, N);
+
+ reftable_stack_destroy(st);
+ reftable_iterator_destroy(&it);
+ for (i = 0; i < N; i++) {
+ reftable_ref_record_release(&refs[i]);
+ reftable_log_record_release(&logs[i]);
+ }
+ clear_dir(dir);
+}
+
+void test_reftable_stack__log_normalize(void)
+{
+ struct reftable_write_options opts = {
+ 0,
+ };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_log_record input = {
+ .refname = (char *) "branch",
+ .update_index = 1,
+ .value_type = REFTABLE_LOG_UPDATE,
+ .value = {
+ .update = {
+ .new_hash = { 1 },
+ .old_hash = { 2 },
+ },
+ },
+ };
+ struct reftable_log_record dest = {
+ .update_index = 0,
+ };
+ struct write_log_arg arg = {
+ .log = &input,
+ .update_index = 1,
+ };
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ input.value.update.message = (char *) "one\ntwo";
+ cl_assert_equal_i(reftable_stack_add(st, write_test_log,
+ &arg), REFTABLE_API_ERROR);
+
+ input.value.update.message = (char *) "one";
+ cl_assert(reftable_stack_add(st, write_test_log, &arg) == 0);
+ cl_assert(reftable_stack_read_log(st, input.refname, &dest) == 0);
+ cl_assert_equal_s(dest.value.update.message, "one\n");
+
+ input.value.update.message = (char *) "two\n";
+ arg.update_index = 2;
+ cl_assert(reftable_stack_add(st, write_test_log, &arg) == 0);
+ cl_assert(reftable_stack_read_log(st, input.refname, &dest) == 0);
+ cl_assert_equal_s(dest.value.update.message, "two\n");
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ reftable_log_record_release(&dest);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__tombstone(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ struct reftable_ref_record refs[2] = { 0 };
+ struct reftable_log_record logs[2] = { 0 };
+ size_t i, N = ARRAY_SIZE(refs);
+ struct reftable_ref_record dest = { 0 };
+ struct reftable_log_record log_dest = { 0 };
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ /* even entries add the refs, odd entries delete them. */
+ for (i = 0; i < N; i++) {
+ const char *buf = "branch";
+ refs[i].refname = xstrdup(buf);
+ refs[i].update_index = i + 1;
+ if (i % 2 == 0) {
+ refs[i].value_type = REFTABLE_REF_VAL1;
+ cl_reftable_set_hash(refs[i].value.val1, i,
+ REFTABLE_HASH_SHA1);
+ }
+
+ logs[i].refname = xstrdup(buf);
+ /*
+ * update_index is part of the key so should be constant.
+ * The value itself should be less than the writer's upper
+ * limit.
+ */
+ logs[i].update_index = 1;
+ if (i % 2 == 0) {
+ logs[i].value_type = REFTABLE_LOG_UPDATE;
+ cl_reftable_set_hash(logs[i].value.update.new_hash, i,
+ REFTABLE_HASH_SHA1);
+ logs[i].value.update.email =
+ xstrdup("identity@invalid");
+ }
+ }
+ for (i = 0; i < N; i++)
+ cl_assert(reftable_stack_add(st, write_test_ref, &refs[i]) == 0);
+
+ for (i = 0; i < N; i++) {
+ struct write_log_arg arg = {
+ .log = &logs[i],
+ .update_index = reftable_stack_next_update_index(st),
+ };
+ cl_assert(reftable_stack_add(st, write_test_log, &arg) == 0);
+ }
+
+ cl_assert_equal_i(reftable_stack_read_ref(st, "branch", &dest), 1);
+ reftable_ref_record_release(&dest);
+
+ cl_assert_equal_i(reftable_stack_read_log(st, "branch", &log_dest), 1);
+ reftable_log_record_release(&log_dest);
+
+ cl_assert(reftable_stack_compact_all(st, NULL) == 0);
+ cl_assert_equal_i(reftable_stack_read_ref(st, "branch", &dest), 1);
+ cl_assert_equal_i(reftable_stack_read_log(st, "branch", &log_dest), 1);
+ reftable_ref_record_release(&dest);
+ reftable_log_record_release(&log_dest);
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ for (i = 0; i < N; i++) {
+ reftable_ref_record_release(&refs[i]);
+ reftable_log_record_release(&logs[i]);
+ }
+ clear_dir(dir);
+}
+
+void test_reftable_stack__hash_id(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+
+ struct reftable_ref_record ref = {
+ .refname = (char *) "master",
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "target",
+ .update_index = 1,
+ };
+ struct reftable_write_options opts32 = { .hash_id = REFTABLE_HASH_SHA256 };
+ struct reftable_stack *st32 = NULL;
+ struct reftable_write_options opts_default = { 0 };
+ struct reftable_stack *st_default = NULL;
+ struct reftable_ref_record dest = { 0 };
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref) == 0);
+
+ /* can't read it with the wrong hash ID. */
+ cl_assert_equal_i(reftable_new_stack(&st32, dir, &opts32), REFTABLE_FORMAT_ERROR);
+
+ /* check that we can read it back with default opts too. */
+ cl_assert(reftable_new_stack(&st_default, dir, &opts_default) == 0);
+ cl_assert(reftable_stack_read_ref(st_default, "master", &dest) == 0);
+ cl_assert(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1) != 0);
+ reftable_ref_record_release(&dest);
+ reftable_stack_destroy(st);
+ reftable_stack_destroy(st_default);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__suggest_compaction_segment(void)
+{
+ uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 };
+ struct segment min =
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
+ cl_assert_equal_i(min.start, 1);
+ cl_assert_equal_i(min.end, 10);
+}
+
+void test_reftable_stack__suggest_compaction_segment_nothing(void)
+{
+ uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
+ struct segment result =
+ suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2);
+ cl_assert_equal_i(result.start, result.end);
+}
+
+void test_reftable_stack__reflog_expire(void)
+{
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ struct reftable_log_record logs[20] = { 0 };
+ size_t i, N = ARRAY_SIZE(logs) - 1;
+ struct reftable_log_expiry_config expiry = {
+ .time = 10,
+ };
+ struct reftable_log_record log = { 0 };
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (i = 1; i <= N; i++) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "branch%02"PRIuMAX, (uintmax_t)i);
+
+ logs[i].refname = xstrdup(buf);
+ logs[i].update_index = i;
+ logs[i].value_type = REFTABLE_LOG_UPDATE;
+ logs[i].value.update.time = i;
+ logs[i].value.update.email = xstrdup("identity@invalid");
+ cl_reftable_set_hash(logs[i].value.update.new_hash, i,
+ REFTABLE_HASH_SHA1);
+ }
+
+ for (i = 1; i <= N; i++) {
+ struct write_log_arg arg = {
+ .log = &logs[i],
+ .update_index = reftable_stack_next_update_index(st),
+ };
+ cl_assert(reftable_stack_add(st, write_test_log, &arg) == 0);
+ }
+
+ cl_assert(reftable_stack_compact_all(st, NULL) == 0);
+ cl_assert(reftable_stack_compact_all(st, &expiry) == 0);
+ cl_assert_equal_i(reftable_stack_read_log(st, logs[9].refname, &log), 1);
+ cl_assert(reftable_stack_read_log(st, logs[11].refname, &log) == 0);
+
+ expiry.min_update_index = 15;
+ cl_assert(reftable_stack_compact_all(st, &expiry) == 0);
+ cl_assert_equal_i(reftable_stack_read_log(st, logs[14].refname, &log), 1);
+ cl_assert(reftable_stack_read_log(st, logs[16].refname, &log) == 0);
+
+ /* cleanup */
+ reftable_stack_destroy(st);
+ for (i = 0; i <= N; i++)
+ reftable_log_record_release(&logs[i]);
+ clear_dir(dir);
+ reftable_log_record_release(&log);
+}
+
+static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)
+{
+ cl_assert(reftable_writer_set_limits(wr, 1, 1) == 0);
+ return 0;
+}
+
+void test_reftable_stack__empty_add(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_stack *st2 = NULL;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ cl_assert(reftable_stack_add(st, write_nothing, NULL) == 0);
+ cl_assert(reftable_new_stack(&st2, dir, &opts) == 0);
+ clear_dir(dir);
+ reftable_stack_destroy(st);
+ reftable_stack_destroy(st2);
+}
+
+static int fastlogN(uint64_t sz, uint64_t N)
+{
+ int l = 0;
+ if (sz == 0)
+ return 0;
+ for (; sz; sz /= N)
+ l++;
+ return l - 1;
+}
+
+void test_reftable_stack__auto_compaction(void)
+{
+ struct reftable_write_options opts = {
+ .disable_auto_compact = 1,
+ };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ size_t i, N = 100;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (i = 0; i < N; i++) {
+ char name[100];
+ struct reftable_ref_record ref = {
+ .refname = name,
+ .update_index = reftable_stack_next_update_index(st),
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
+
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref) == 0);
+ cl_assert(reftable_stack_auto_compact(st) == 0);
+ cl_assert(i < 2 || st->merged->readers_len < 2 * fastlogN(i, 2));
+ }
+
+ cl_assert(reftable_stack_compaction_stats(st)->entries_written <
+ (uint64_t)(N * fastlogN(N, 2)));
+
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__auto_compaction_factor(void)
+{
+ struct reftable_write_options opts = {
+ .auto_compaction_factor = 5,
+ };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ size_t N = 100;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (size_t i = 0; i < N; i++) {
+ char name[20];
+ struct reftable_ref_record ref = {
+ .refname = name,
+ .update_index = reftable_stack_next_update_index(st),
+ .value_type = REFTABLE_REF_VAL1,
+ };
+ xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i);
+
+ cl_assert(reftable_stack_add(st, &write_test_ref, &ref) == 0);
+ cl_assert(i < 5 || st->merged->readers_len < 5 * fastlogN(i, 5));
+ }
+
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__auto_compaction_with_locked_tables(void)
+{
+ struct reftable_write_options opts = {
+ .disable_auto_compact = 1,
+ };
+ struct reftable_stack *st = NULL;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ char *dir = get_tmp_dir(__LINE__);
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ write_n_ref_tables(st, 5);
+ cl_assert_equal_i(st->merged->readers_len, 5);
+
+ /*
+ * Given that all tables we have written should be roughly the same
+ * size, we expect that auto-compaction will want to compact all of the
+ * tables. Locking any of the tables will keep it from doing so.
+ */
+ cl_assert(reftable_buf_addstr(&buf, dir) == 0);
+ cl_assert(reftable_buf_addstr(&buf, "/") == 0);
+ cl_assert(reftable_buf_addstr(&buf, st->readers[2]->name) == 0);
+ cl_assert(reftable_buf_addstr(&buf, ".lock") == 0);
+ write_file_buf(buf.buf, "", 0);
+
+ /*
+ * When parts of the stack are locked, then auto-compaction does a best
+ * effort compaction of those tables which aren't locked. So while this
+ * would in theory compact all tables, due to the preexisting lock we
+ * only compact the newest two tables.
+ */
+ cl_assert(reftable_stack_auto_compact(st) == 0);
+ cl_assert_equal_i(st->stats.failures, 0);
+ cl_assert_equal_i(st->merged->readers_len, 4);
+
+ reftable_stack_destroy(st);
+ reftable_buf_release(&buf);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__add_performs_auto_compaction(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ size_t i, n = 20;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ for (i = 0; i <= n; i++) {
+ struct reftable_ref_record ref = {
+ .update_index = reftable_stack_next_update_index(st),
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ char buf[128];
+
+ /*
+ * Disable auto-compaction for all but the last runs. Like this
+ * we can ensure that we indeed honor this setting and have
+ * better control over when exactly auto compaction runs.
+ */
+ st->opts.disable_auto_compact = i != n;
+
+ snprintf(buf, sizeof(buf), "branch-%04"PRIuMAX, (uintmax_t)i);
+ ref.refname = buf;
+
+ cl_assert(reftable_stack_add(st, write_test_ref, &ref) == 0);
+
+ /*
+ * The stack length should grow continuously for all runs where
+ * auto compaction is disabled. When enabled, we should merge
+ * all tables in the stack.
+ */
+ if (i != n)
+ cl_assert_equal_i(st->merged->readers_len, i + 1);
+ else
+ cl_assert_equal_i(st->merged->readers_len, 1);
+ }
+
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__compaction_with_locked_tables(void)
+{
+ struct reftable_write_options opts = {
+ .disable_auto_compact = 1,
+ };
+ struct reftable_stack *st = NULL;
+ struct reftable_buf buf = REFTABLE_BUF_INIT;
+ char *dir = get_tmp_dir(__LINE__);
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ write_n_ref_tables(st, 3);
+ cl_assert_equal_i(st->merged->readers_len, 3);
+
+ /* Lock one of the tables that we're about to compact. */
+ cl_assert(reftable_buf_addstr(&buf, dir) == 0);
+ cl_assert(reftable_buf_addstr(&buf, "/") == 0);
+ cl_assert(reftable_buf_addstr(&buf, st->readers[1]->name) == 0);
+ cl_assert(reftable_buf_addstr(&buf, ".lock") == 0);
+ write_file_buf(buf.buf, "", 0);
+
+ /*
+ * Compaction is expected to fail given that we were not able to
+ * compact all tables.
+ */
+ cl_assert_equal_i(reftable_stack_compact_all(st, NULL), REFTABLE_LOCK_ERROR);
+ cl_assert_equal_i(st->stats.failures, 1);
+ cl_assert_equal_i(st->merged->readers_len, 3);
+
+ reftable_stack_destroy(st);
+ reftable_buf_release(&buf);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__compaction_concurrent(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st1 = NULL, *st2 = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+
+ cl_assert(reftable_new_stack(&st1, dir, &opts) == 0);
+ write_n_ref_tables(st1, 3);
+
+ cl_assert(reftable_new_stack(&st2, dir, &opts) == 0);
+ cl_assert(reftable_stack_compact_all(st1, NULL) == 0);
+
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
+
+ cl_assert_equal_i(count_dir_entries(dir), 2);
+ clear_dir(dir);
+}
+
+static void unclean_stack_close(struct reftable_stack *st)
+{
+ /* break abstraction boundary to simulate unclean shutdown. */
+ for (size_t i = 0; i < st->readers_len; i++)
+ reftable_reader_decref(st->readers[i]);
+ st->readers_len = 0;
+ REFTABLE_FREE_AND_NULL(st->readers);
+}
+
+void test_reftable_stack__compaction_concurrent_clean(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+
+ cl_assert(reftable_new_stack(&st1, dir, &opts) == 0);
+ write_n_ref_tables(st1, 3);
+
+ cl_assert(reftable_new_stack(&st2, dir, &opts) == 0);
+ cl_assert(reftable_stack_compact_all(st1, NULL) == 0);
+
+ unclean_stack_close(st1);
+ unclean_stack_close(st2);
+
+ cl_assert(reftable_new_stack(&st3, dir, &opts) == 0);
+ cl_assert(reftable_stack_clean(st3) == 0);
+ cl_assert_equal_i(count_dir_entries(dir), 2);
+
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
+ reftable_stack_destroy(st3);
+
+ clear_dir(dir);
+}
+
+void test_reftable_stack__read_across_reload(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st1 = NULL, *st2 = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+ char *dir = get_tmp_dir(__LINE__);
+
+ /* Create a first stack and set up an iterator for it. */
+ cl_assert(reftable_new_stack(&st1, dir, &opts) == 0);
+ write_n_ref_tables(st1, 2);
+ cl_assert_equal_i(st1->merged->readers_len, 2);
+ reftable_stack_init_ref_iterator(st1, &it);
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+
+ /* Set up a second stack for the same directory and compact it. */
+ cl_assert(reftable_new_stack(&st2, dir, &opts) == 0);
+ cl_assert_equal_i(st2->merged->readers_len, 2);
+ cl_assert(reftable_stack_compact_all(st2, NULL) == 0);
+ cl_assert_equal_i(st2->merged->readers_len, 1);
+
+ /*
+ * Verify that we can continue to use the old iterator even after we
+ * have reloaded its stack.
+ */
+ cl_assert(reftable_stack_reload(st1) == 0);
+ cl_assert_equal_i(st1->merged->readers_len, 1);
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_s(rec.refname, "refs/heads/branch-0000");
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_s(rec.refname, "refs/heads/branch-0001");
+ cl_assert(reftable_iterator_next_ref(&it, &rec) > 0);
+
+ reftable_ref_record_release(&rec);
+ reftable_iterator_destroy(&it);
+ reftable_stack_destroy(st1);
+ reftable_stack_destroy(st2);
+ clear_dir(dir);
+}
+
+void test_reftable_stack__reload_with_missing_table(void)
+{
+ struct reftable_write_options opts = { 0 };
+ struct reftable_stack *st = NULL;
+ struct reftable_ref_record rec = { 0 };
+ struct reftable_iterator it = { 0 };
+ struct reftable_buf table_path = REFTABLE_BUF_INIT, content = REFTABLE_BUF_INIT;
+ char *dir = get_tmp_dir(__LINE__);
+
+ /* Create a first stack and set up an iterator for it. */
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+ write_n_ref_tables(st, 2);
+ cl_assert_equal_i(st->merged->readers_len, 2);
+ reftable_stack_init_ref_iterator(st, &it);
+ cl_assert(reftable_iterator_seek_ref(&it, "") == 0);
+
+ /*
+ * Update the tables.list file with some garbage data, while reusing
+ * our old readers. This should trigger a partial reload of the stack,
+ * where we try to reuse our old readers.
+ */
+ cl_assert(reftable_buf_addstr(&content, st->readers[0]->name) == 0);
+ cl_assert(reftable_buf_addstr(&content, "\n") == 0);
+ cl_assert(reftable_buf_addstr(&content, st->readers[1]->name) == 0);
+ cl_assert(reftable_buf_addstr(&content, "\n") == 0);
+ cl_assert(reftable_buf_addstr(&content, "garbage\n") == 0);
+ cl_assert(reftable_buf_addstr(&table_path, st->list_file) == 0);
+ cl_assert(reftable_buf_addstr(&table_path, ".lock") == 0);
+ write_file_buf(table_path.buf, content.buf, content.len);
+ cl_assert(rename(table_path.buf, st->list_file) == 0);
+
+ cl_assert_equal_i(reftable_stack_reload(st), -4);
+ cl_assert_equal_i(st->merged->readers_len, 2);
+
+ /*
+ * Even though the reload has failed, we should be able to continue
+ * using the iterator.
+ */
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_s(rec.refname, "refs/heads/branch-0000");
+ cl_assert(reftable_iterator_next_ref(&it, &rec) == 0);
+ cl_assert_equal_s(rec.refname, "refs/heads/branch-0001");
+ cl_assert(reftable_iterator_next_ref(&it, &rec) > 0);
+
+ reftable_ref_record_release(&rec);
+ reftable_iterator_destroy(&it);
+ reftable_stack_destroy(st);
+ reftable_buf_release(&table_path);
+ reftable_buf_release(&content);
+ clear_dir(dir);
+}
+
+static int write_limits_after_ref(struct reftable_writer *wr, void *arg)
+{
+ struct reftable_ref_record *ref = arg;
+ cl_assert(reftable_writer_set_limits(wr, ref->update_index,
+ ref->update_index) == 0);
+ cl_assert(reftable_writer_add_ref(wr, ref) == 0);
+ return reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
+}
+
+void test_reftable_stack__invalid_limit_updates(void)
+{
+ struct reftable_ref_record ref = {
+ .refname = (char *) "HEAD",
+ .update_index = 1,
+ .value_type = REFTABLE_REF_SYMREF,
+ .value.symref = (char *) "master",
+ };
+ struct reftable_write_options opts = {
+ .default_permissions = 0660,
+ };
+ struct reftable_addition *add = NULL;
+ char *dir = get_tmp_dir(__LINE__);
+ struct reftable_stack *st = NULL;
+
+ cl_assert(reftable_new_stack(&st, dir, &opts) == 0);
+
+ reftable_addition_destroy(add);
+
+ cl_assert(reftable_stack_new_addition(&add, st, 0) == 0);
+
+ /*
+ * write_limits_after_ref also updates the update indexes after adding
+ * the record. This should cause an err to be returned, since the limits
+ * must be set at the start.
+ */
+ cl_assert_equal_i(reftable_addition_add(add, write_limits_after_ref, &ref),
+ REFTABLE_API_ERROR);
+
+ reftable_addition_destroy(add);
+ reftable_stack_destroy(st);
+ clear_dir(dir);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 09/10] t/unit-tests: convert reftable stack test to use clar
2025-04-29 17:53 ` [PATCH v2 09/10] t/unit-tests: convert reftable stack " Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
2025-05-05 9:11 ` Seyi Chamber
0 siblings, 1 reply; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:53:01PM +0100, Seyi Kuforiji wrote:
> diff --git a/t/meson.build b/t/meson.build
> index 756cb2a2dd..8fa00fc9ef 100644
> --- a/t/meson.build
> +++ b/t/meson.build
> @@ -1102,7 +1102,6 @@ integration_tests = [
> # sufficient to catch missing test suites in our CI though.
> foreach glob, tests : {
> 't[0-9][0-9][0-9][0-9]-*.sh': integration_tests,
> - 'unit-tests/t-*.c': unit_test_programs,
> 'unit-tests/u-*.c': clar_test_suites,
> }
> actual_tests = run_command(shell, '-c', 'ls ' + glob,
Okay. Ideally this would be moved into the next commit where we remove
the infrastructure for our old-style unit tests, but we can't because
the glob matches nothing anymore and thus causes an error.
Also, we have a "check-meson" target in "t/Makefile". Don't we have to
remove unit tests from there, too?
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 09/10] t/unit-tests: convert reftable stack test to use clar
2025-05-02 9:57 ` Patrick Steinhardt
@ 2025-05-05 9:11 ` Seyi Chamber
2025-05-05 9:52 ` Patrick Steinhardt
0 siblings, 1 reply; 31+ messages in thread
From: Seyi Chamber @ 2025-05-05 9:11 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, phillip.wood
On Fri, 2 May 2025 at 10:57, Patrick Steinhardt <ps@pks.im> wrote:
>
> On Tue, Apr 29, 2025 at 06:53:01PM +0100, Seyi Kuforiji wrote:
> > diff --git a/t/meson.build b/t/meson.build
> > index 756cb2a2dd..8fa00fc9ef 100644
> > --- a/t/meson.build
> > +++ b/t/meson.build
> > @@ -1102,7 +1102,6 @@ integration_tests = [
> > # sufficient to catch missing test suites in our CI though.
> > foreach glob, tests : {
> > 't[0-9][0-9][0-9][0-9]-*.sh': integration_tests,
> > - 'unit-tests/t-*.c': unit_test_programs,
> > 'unit-tests/u-*.c': clar_test_suites,
> > }
> > actual_tests = run_command(shell, '-c', 'ls ' + glob,
>
> Okay. Ideally this would be moved into the next commit where we remove
> the infrastructure for our old-style unit tests, but we can't because
> the glob matches nothing anymore and thus causes an error.
>
> Also, we have a "check-meson" target in "t/Makefile". Don't we have to
> remove unit tests from there, too?
>
> Patrick
You are referring to this, yes?
`@# awk acts up when trying to match single quotes, so we use \047 instead.
@mkdir -p mesontmp && \
printf "%s\n" \
"integration_tests t[0-9][0-9][0-9][0-9]-*.sh" \
"unit_test_programs unit-tests/t-*.c" \
"clar_test_suites unit-tests/u-*.c" | \
while read -r variable pattern; do \`
I've hardly taken a look at the `t/Makefile`, given my very little
interaction with the file throughout the test conversions.
Best
Seyi
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 09/10] t/unit-tests: convert reftable stack test to use clar
2025-05-05 9:11 ` Seyi Chamber
@ 2025-05-05 9:52 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-05 9:52 UTC (permalink / raw)
To: Seyi Chamber; +Cc: git, phillip.wood
On Mon, May 05, 2025 at 10:11:18AM +0100, Seyi Chamber wrote:
> On Fri, 2 May 2025 at 10:57, Patrick Steinhardt <ps@pks.im> wrote:
> >
> > On Tue, Apr 29, 2025 at 06:53:01PM +0100, Seyi Kuforiji wrote:
> > > diff --git a/t/meson.build b/t/meson.build
> > > index 756cb2a2dd..8fa00fc9ef 100644
> > > --- a/t/meson.build
> > > +++ b/t/meson.build
> > > @@ -1102,7 +1102,6 @@ integration_tests = [
> > > # sufficient to catch missing test suites in our CI though.
> > > foreach glob, tests : {
> > > 't[0-9][0-9][0-9][0-9]-*.sh': integration_tests,
> > > - 'unit-tests/t-*.c': unit_test_programs,
> > > 'unit-tests/u-*.c': clar_test_suites,
> > > }
> > > actual_tests = run_command(shell, '-c', 'ls ' + glob,
> >
> > Okay. Ideally this would be moved into the next commit where we remove
> > the infrastructure for our old-style unit tests, but we can't because
> > the glob matches nothing anymore and thus causes an error.
> >
> > Also, we have a "check-meson" target in "t/Makefile". Don't we have to
> > remove unit tests from there, too?
> >
> > Patrick
>
> You are referring to this, yes?
> `@# awk acts up when trying to match single quotes, so we use \047 instead.
> @mkdir -p mesontmp && \
> printf "%s\n" \
> "integration_tests t[0-9][0-9][0-9][0-9]-*.sh" \
> "unit_test_programs unit-tests/t-*.c" \
> "clar_test_suites unit-tests/u-*.c" | \
> while read -r variable pattern; do \`
>
> I've hardly taken a look at the `t/Makefile`, given my very little
> interaction with the file throughout the test conversions.
Yes, that's the part I was referring to.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar
2025-04-29 17:52 [PATCH v2 00/10] t/unit-tests: convert unit-tests to use clar Seyi Kuforiji
` (8 preceding siblings ...)
2025-04-29 17:53 ` [PATCH v2 09/10] t/unit-tests: convert reftable stack " Seyi Kuforiji
@ 2025-04-29 17:53 ` Seyi Kuforiji
2025-05-02 9:57 ` Patrick Steinhardt
9 siblings, 1 reply; 31+ messages in thread
From: Seyi Kuforiji @ 2025-04-29 17:53 UTC (permalink / raw)
To: git; +Cc: ps, phillip.wood, Seyi Kuforiji
Helper functions defined in `t/unit-tests/lib-reftable.{c,h}` are
required for the reftable-related test files to run efficeintly. In the
current implementation these functions are designed to conform with our
homegrown unit-testing structure. So in other to convert the reftable
test files, there is need for a clar specific implementation of these
helper functions.
type cast `for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++)`
Adapt functions in lib-reftable.{c,h} to use clar. These functions
conform with the clar testing framework and become available for all
reftable-related test files implemented using the clar testing
framework, which requires them. This change migrates the helper
functions back into `lib-reftable.{c,h}`.
Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
---
Makefile | 4 +-
t/meson.build | 4 +-
t/unit-tests/lib-reftable.c | 26 +++++------
t/unit-tests/lib-reftable.h | 6 +--
t/unit-tests/unit-test.c | 93 -------------------------------------
t/unit-tests/unit-test.h | 16 -------
6 files changed, 20 insertions(+), 129 deletions(-)
diff --git a/Makefile b/Makefile
index 0b42893611..7e646e16ee 100644
--- a/Makefile
+++ b/Makefile
@@ -1379,12 +1379,12 @@ CLAR_TEST_SUITES += u-urlmatch-normalization
CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
-CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
+CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
+CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
-UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
# xdiff and reftable libs may in turn depend on what is in libgit.a
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
diff --git a/t/meson.build b/t/meson.build
index 8fa00fc9ef..7c305a90b5 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -26,8 +26,9 @@ clar_test_suites = [
clar_sources = [
'unit-tests/clar/clar.c',
+ 'unit-tests/lib-oid.c',
+ 'unit-tests/lib-reftable.c',
'unit-tests/unit-test.c',
- 'unit-tests/lib-oid.c'
]
clar_decls_h = custom_target(
@@ -69,7 +70,6 @@ foreach unit_test_program : unit_test_programs
unit_test = executable(unit_test_name,
sources: [
'unit-tests/test-lib.c',
- 'unit-tests/lib-reftable.c',
unit_test_program,
],
dependencies: [libgit_commonmain],
diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c
index 8a69612266..414364166f 100644
--- a/t/unit-tests/lib-reftable.c
+++ b/t/unit-tests/lib-reftable.c
@@ -1,12 +1,12 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "lib-reftable.h"
-#include "test-lib.h"
+#include "unit-test.h"
#include "reftable/constants.h"
#include "reftable/writer.h"
#include "strbuf.h"
-void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id)
+void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id)
{
memset(p, (uint8_t)i, hash_size(id));
}
@@ -22,17 +22,17 @@ static int strbuf_writer_flush(void *arg UNUSED)
return 0;
}
-struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
+struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
struct reftable_write_options *opts)
{
struct reftable_writer *writer;
int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush,
buf, opts);
- check(!ret);
+ cl_assert(ret == 0);
return writer;
}
-void t_reftable_write_to_buf(struct reftable_buf *buf,
+void cl_reftable_write_to_buf(struct reftable_buf *buf,
struct reftable_ref_record *refs,
size_t nrefs,
struct reftable_log_record *logs,
@@ -64,35 +64,35 @@ void t_reftable_write_to_buf(struct reftable_buf *buf,
min = ui;
}
- writer = t_reftable_strbuf_writer(buf, &opts);
+ writer = cl_reftable_strbuf_writer(buf, &opts);
reftable_writer_set_limits(writer, min, max);
if (nrefs) {
ret = reftable_writer_add_refs(writer, refs, nrefs);
- check_int(ret, ==, 0);
+ cl_assert_equal_i(ret, 0);
}
if (nlogs) {
ret = reftable_writer_add_logs(writer, logs, nlogs);
- check_int(ret, ==, 0);
+ cl_assert_equal_i(ret, 0);
}
ret = reftable_writer_close(writer);
- check_int(ret, ==, 0);
+ cl_assert_equal_i(ret, 0);
stats = reftable_writer_stats(writer);
- for (size_t i = 0; i < stats->ref_stats.blocks; i++) {
+ for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++) {
size_t off = i * (opts.block_size ? opts.block_size
: DEFAULT_BLOCK_SIZE);
if (!off)
off = header_size(opts.hash_id == REFTABLE_HASH_SHA256 ? 2 : 1);
- check_char(buf->buf[off], ==, 'r');
+ cl_assert(buf->buf[off] == 'r');
}
if (nrefs)
- check_int(stats->ref_stats.blocks, >, 0);
+ cl_assert(stats->ref_stats.blocks > 0);
if (nlogs)
- check_int(stats->log_stats.blocks, >, 0);
+ cl_assert(stats->log_stats.blocks > 0);
reftable_writer_free(writer);
}
diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h
index e4c360fa7e..2958db5dc0 100644
--- a/t/unit-tests/lib-reftable.h
+++ b/t/unit-tests/lib-reftable.h
@@ -6,12 +6,12 @@
struct reftable_buf;
-void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
+void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
-struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
+struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
struct reftable_write_options *opts);
-void t_reftable_write_to_buf(struct reftable_buf *buf,
+void cl_reftable_write_to_buf(struct reftable_buf *buf,
struct reftable_ref_record *refs,
size_t nrecords,
struct reftable_log_record *logs,
diff --git a/t/unit-tests/unit-test.c b/t/unit-tests/unit-test.c
index 6c2a4e6aa8..5af645048a 100644
--- a/t/unit-tests/unit-test.c
+++ b/t/unit-tests/unit-test.c
@@ -1,103 +1,10 @@
#include "unit-test.h"
#include "hex.h"
#include "parse-options.h"
-#include "reftable/constants.h"
-#include "reftable/writer.h"
#include "strbuf.h"
#include "string-list.h"
#include "strvec.h"
-void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id)
-{
- memset(p, (uint8_t)i, hash_size(id));
-}
-
-static ssize_t strbuf_writer_write(void *b, const void *data, size_t sz)
-{
- strbuf_add(b, data, sz);
- return sz;
-}
-
-static int strbuf_writer_flush(void *arg UNUSED)
-{
- return 0;
-}
-
-struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
- struct reftable_write_options *opts)
-{
- struct reftable_writer *writer;
- int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush,
- buf, opts);
- cl_assert(ret == 0);
- return writer;
-}
-
-void cl_reftable_write_to_buf(struct reftable_buf *buf,
- struct reftable_ref_record *refs,
- size_t nrefs,
- struct reftable_log_record *logs,
- size_t nlogs,
- struct reftable_write_options *_opts)
-{
- struct reftable_write_options opts = { 0 };
- const struct reftable_stats *stats;
- struct reftable_writer *writer;
- uint64_t min = 0xffffffff;
- uint64_t max = 0;
- int ret;
-
- if (_opts)
- opts = *_opts;
-
- for (size_t i = 0; i < nrefs; i++) {
- uint64_t ui = refs[i].update_index;
- if (ui > max)
- max = ui;
- if (ui < min)
- min = ui;
- }
- for (size_t i = 0; i < nlogs; i++) {
- uint64_t ui = logs[i].update_index;
- if (ui > max)
- max = ui;
- if (ui < min)
- min = ui;
- }
-
- writer = cl_reftable_strbuf_writer(buf, &opts);
- reftable_writer_set_limits(writer, min, max);
-
- if (nrefs) {
- ret = reftable_writer_add_refs(writer, refs, nrefs);
- cl_assert_equal_i(ret, 0);
- }
-
- if (nlogs) {
- ret = reftable_writer_add_logs(writer, logs, nlogs);
- cl_assert_equal_i(ret, 0);
- }
-
- ret = reftable_writer_close(writer);
- cl_assert_equal_i(ret, 0);
-
- stats = reftable_writer_stats(writer);
- for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++) {
- size_t off = i * (opts.block_size ? opts.block_size
- : DEFAULT_BLOCK_SIZE);
- if (!off)
- off = header_size(opts.hash_id == REFTABLE_HASH_SHA256 ? 2 : 1);
- cl_assert(buf->buf[off] == 'r');
- }
-
- if (nrefs)
- cl_assert(stats->ref_stats.blocks > 0);
- if (nlogs)
- cl_assert(stats->log_stats.blocks > 0);
-
- reftable_writer_free(writer);
-}
-
static const char * const unit_test_usage[] = {
N_("unit-test [<options>]"),
NULL,
diff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h
index fe0aebd876..85e5d6a948 100644
--- a/t/unit-tests/unit-test.h
+++ b/t/unit-tests/unit-test.h
@@ -1,24 +1,8 @@
#include "git-compat-util.h"
#include "clar/clar.h"
#include "clar-decls.h"
-#include "git-compat-util.h"
-#include "reftable/reftable-writer.h"
#include "strbuf.h"
-struct reftable_buf;
-
-void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
-
-struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
- struct reftable_write_options *opts);
-
-void cl_reftable_write_to_buf(struct reftable_buf *buf,
- struct reftable_ref_record *refs,
- size_t nrecords,
- struct reftable_log_record *logs,
- size_t nlogs,
- struct reftable_write_options *opts);
-
#define cl_failf(fmt, ...) do { \
char desc[4096]; \
snprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \
--
2.43.0
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar
2025-04-29 17:53 ` [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar Seyi Kuforiji
@ 2025-05-02 9:57 ` Patrick Steinhardt
2025-05-05 7:27 ` Seyi Chamber
0 siblings, 1 reply; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-02 9:57 UTC (permalink / raw)
To: Seyi Kuforiji; +Cc: git, phillip.wood
On Tue, Apr 29, 2025 at 06:53:02PM +0100, Seyi Kuforiji wrote:
> Helper functions defined in `t/unit-tests/lib-reftable.{c,h}` are
> required for the reftable-related test files to run efficeintly. In the
> current implementation these functions are designed to conform with our
> homegrown unit-testing structure. So in other to convert the reftable
> test files, there is need for a clar specific implementation of these
> helper functions.
>
> type cast `for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++)`
> Adapt functions in lib-reftable.{c,h} to use clar. These functions
> conform with the clar testing framework and become available for all
> reftable-related test files implemented using the clar testing
> framework, which requires them. This change migrates the helper
> functions back into `lib-reftable.{c,h}`.
>
> Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
> ---
> Makefile | 4 +-
> t/meson.build | 4 +-
> t/unit-tests/lib-reftable.c | 26 +++++------
> t/unit-tests/lib-reftable.h | 6 +--
> t/unit-tests/unit-test.c | 93 -------------------------------------
> t/unit-tests/unit-test.h | 16 -------
> 6 files changed, 20 insertions(+), 129 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index 0b42893611..7e646e16ee 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1379,12 +1379,12 @@ CLAR_TEST_SUITES += u-urlmatch-normalization
> CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
> CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))
> CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
> -CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
> CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
> +CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
> +CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
>
> UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
> UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
> -UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
>
`UNIT_TEST_PROGS` only contains the "unit-test" binary now, right? Does
this mean that we can simplify build rules in our Makefile now?
> diff --git a/t/meson.build b/t/meson.build
> index 8fa00fc9ef..7c305a90b5 100644
> --- a/t/meson.build
> +++ b/t/meson.build
> @@ -26,8 +26,9 @@ clar_test_suites = [
>
> clar_sources = [
> 'unit-tests/clar/clar.c',
> + 'unit-tests/lib-oid.c',
> + 'unit-tests/lib-reftable.c',
> 'unit-tests/unit-test.c',
> - 'unit-tests/lib-oid.c'
> ]
>
> clar_decls_h = custom_target(
> @@ -69,7 +70,6 @@ foreach unit_test_program : unit_test_programs
> unit_test = executable(unit_test_name,
> sources: [
> 'unit-tests/test-lib.c',
> - 'unit-tests/lib-reftable.c',
> unit_test_program,
> ],
> dependencies: [libgit_commonmain],
Can't we remove this completely? `unit_test_programs` is empty now, so
this loop is a no-op now.
> diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h
> index e4c360fa7e..2958db5dc0 100644
> --- a/t/unit-tests/lib-reftable.h
> +++ b/t/unit-tests/lib-reftable.h
> @@ -6,12 +6,12 @@
>
> struct reftable_buf;
>
> -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
>
> -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
> +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
> struct reftable_write_options *opts);
>
> -void t_reftable_write_to_buf(struct reftable_buf *buf,
> +void cl_reftable_write_to_buf(struct reftable_buf *buf,
> struct reftable_ref_record *refs,
> size_t nrecords,
> struct reftable_log_record *logs,
It is quite weird that we declare the replacement functions in
"unit-test.h" in the first commit only to remove them at a later point.
It would make way more sense if we introduced the functions in
"t/unit/lib-reftable.{c,h}" right from the start and then only remove
the unused functions in the last step.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar
2025-05-02 9:57 ` Patrick Steinhardt
@ 2025-05-05 7:27 ` Seyi Chamber
2025-05-05 9:52 ` Patrick Steinhardt
0 siblings, 1 reply; 31+ messages in thread
From: Seyi Chamber @ 2025-05-05 7:27 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, phillip.wood
Hi Patrick,
On Fri, 2 May 2025 at 10:58, Patrick Steinhardt <ps@pks.im> wrote:
>
> On Tue, Apr 29, 2025 at 06:53:02PM +0100, Seyi Kuforiji wrote:
> > Helper functions defined in `t/unit-tests/lib-reftable.{c,h}` are
> > required for the reftable-related test files to run efficeintly. In the
> > current implementation these functions are designed to conform with our
> > homegrown unit-testing structure. So in other to convert the reftable
> > test files, there is need for a clar specific implementation of these
> > helper functions.
> >
> > type cast `for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++)`
> > Adapt functions in lib-reftable.{c,h} to use clar. These functions
> > conform with the clar testing framework and become available for all
> > reftable-related test files implemented using the clar testing
> > framework, which requires them. This change migrates the helper
> > functions back into `lib-reftable.{c,h}`.
> >
> > Signed-off-by: Seyi Kuforiji <kuforiji98@gmail.com>
> > ---
> > Makefile | 4 +-
> > t/meson.build | 4 +-
> > t/unit-tests/lib-reftable.c | 26 +++++------
> > t/unit-tests/lib-reftable.h | 6 +--
> > t/unit-tests/unit-test.c | 93 -------------------------------------
> > t/unit-tests/unit-test.h | 16 -------
> > 6 files changed, 20 insertions(+), 129 deletions(-)
> >
> > diff --git a/Makefile b/Makefile
> > index 0b42893611..7e646e16ee 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -1379,12 +1379,12 @@ CLAR_TEST_SUITES += u-urlmatch-normalization
> > CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
> > CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))
> > CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
> > -CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
> > CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
> > +CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
> > +CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
> >
> > UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
> > UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
> > -UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
> >
>
> `UNIT_TEST_PROGS` only contains the "unit-test" binary now, right? Does
> this mean that we can simplify build rules in our Makefile now?
>
> > diff --git a/t/meson.build b/t/meson.build
> > index 8fa00fc9ef..7c305a90b5 100644
> > --- a/t/meson.build
> > +++ b/t/meson.build
> > @@ -26,8 +26,9 @@ clar_test_suites = [
> >
> > clar_sources = [
> > 'unit-tests/clar/clar.c',
> > + 'unit-tests/lib-oid.c',
> > + 'unit-tests/lib-reftable.c',
> > 'unit-tests/unit-test.c',
> > - 'unit-tests/lib-oid.c'
> > ]
> >
> > clar_decls_h = custom_target(
> > @@ -69,7 +70,6 @@ foreach unit_test_program : unit_test_programs
> > unit_test = executable(unit_test_name,
> > sources: [
> > 'unit-tests/test-lib.c',
> > - 'unit-tests/lib-reftable.c',
> > unit_test_program,
> > ],
> > dependencies: [libgit_commonmain],
>
> Can't we remove this completely? `unit_test_programs` is empty now, so
> this loop is a no-op now.
>
> > diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h
> > index e4c360fa7e..2958db5dc0 100644
> > --- a/t/unit-tests/lib-reftable.h
> > +++ b/t/unit-tests/lib-reftable.h
> > @@ -6,12 +6,12 @@
> >
> > struct reftable_buf;
> >
> > -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> >
> > -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
> > +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
> > struct reftable_write_options *opts);
> >
> > -void t_reftable_write_to_buf(struct reftable_buf *buf,
> > +void cl_reftable_write_to_buf(struct reftable_buf *buf,
> > struct reftable_ref_record *refs,
> > size_t nrecords,
> > struct reftable_log_record *logs,
>
> It is quite weird that we declare the replacement functions in
> "unit-test.h" in the first commit only to remove them at a later point.
> It would make way more sense if we introduced the functions in
> "t/unit/lib-reftable.{c,h}" right from the start and then only remove
> the unused functions in the last step.
>
> Patrick
If I get it correctly, you're suggesting I have both the original
functions and the clar-based variant in `t/unit/lib-reftable.{c,h}`
right from the first commit, right?
Thanks
Seyi
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar
2025-05-05 7:27 ` Seyi Chamber
@ 2025-05-05 9:52 ` Patrick Steinhardt
2025-05-26 9:04 ` Seyi Chamber
0 siblings, 1 reply; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-05 9:52 UTC (permalink / raw)
To: Seyi Chamber; +Cc: git, phillip.wood
On Mon, May 05, 2025 at 08:27:16AM +0100, Seyi Chamber wrote:
> On Fri, 2 May 2025 at 10:58, Patrick Steinhardt <ps@pks.im> wrote:
> > On Tue, Apr 29, 2025 at 06:53:02PM +0100, Seyi Kuforiji wrote:
> > > diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h
> > > index e4c360fa7e..2958db5dc0 100644
> > > --- a/t/unit-tests/lib-reftable.h
> > > +++ b/t/unit-tests/lib-reftable.h
> > > @@ -6,12 +6,12 @@
> > >
> > > struct reftable_buf;
> > >
> > > -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > > +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > >
> > > -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
> > > +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
> > > struct reftable_write_options *opts);
> > >
> > > -void t_reftable_write_to_buf(struct reftable_buf *buf,
> > > +void cl_reftable_write_to_buf(struct reftable_buf *buf,
> > > struct reftable_ref_record *refs,
> > > size_t nrecords,
> > > struct reftable_log_record *logs,
> >
> > It is quite weird that we declare the replacement functions in
> > "unit-test.h" in the first commit only to remove them at a later point.
> > It would make way more sense if we introduced the functions in
> > "t/unit/lib-reftable.{c,h}" right from the start and then only remove
> > the unused functions in the last step.
> >
> > Patrick
>
> If I get it correctly, you're suggesting I have both the original
> functions and the clar-based variant in `t/unit/lib-reftable.{c,h}`
Yup, exactly.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar
2025-05-05 9:52 ` Patrick Steinhardt
@ 2025-05-26 9:04 ` Seyi Chamber
2025-05-26 12:56 ` Patrick Steinhardt
0 siblings, 1 reply; 31+ messages in thread
From: Seyi Chamber @ 2025-05-26 9:04 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, phillip.wood
On Mon, 5 May 2025 at 10:52, Patrick Steinhardt <ps@pks.im> wrote:
>
> On Mon, May 05, 2025 at 08:27:16AM +0100, Seyi Chamber wrote:
> > On Fri, 2 May 2025 at 10:58, Patrick Steinhardt <ps@pks.im> wrote:
> > > On Tue, Apr 29, 2025 at 06:53:02PM +0100, Seyi Kuforiji wrote:
> > > > diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h
> > > > index e4c360fa7e..2958db5dc0 100644
> > > > --- a/t/unit-tests/lib-reftable.h
> > > > +++ b/t/unit-tests/lib-reftable.h
> > > > @@ -6,12 +6,12 @@
> > > >
> > > > struct reftable_buf;
> > > >
> > > > -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > > > +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > > >
> > > > -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
> > > > +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
> > > > struct reftable_write_options *opts);
> > > >
> > > > -void t_reftable_write_to_buf(struct reftable_buf *buf,
> > > > +void cl_reftable_write_to_buf(struct reftable_buf *buf,
> > > > struct reftable_ref_record *refs,
> > > > size_t nrecords,
> > > > struct reftable_log_record *logs,
> > >
> > > It is quite weird that we declare the replacement functions in
> > > "unit-test.h" in the first commit only to remove them at a later point.
> > > It would make way more sense if we introduced the functions in
> > > "t/unit/lib-reftable.{c,h}" right from the start and then only remove
> > > the unused functions in the last step.
> > >
> > > Patrick
> >
> > If I get it correctly, you're suggesting I have both the original
> > functions and the clar-based variant in `t/unit/lib-reftable.{c,h}`
>
> Yup, exactly.
>
> Patrick
Hi Patrick,
Thanks for the suggestion to move both the original t-helpers and the
Clar-based cl_ versions into `t/unit/lib-reftable.{c,h}`.
I’ve tried doing that but ran into some build issues. The Clar-based
functions use clar_assert and clar_assert_equal, which aren’t
available to non-Clar tests. Since both sets of helpers would live in
the same object file, this causes linker errors for binaries that
don’t link against Clar.
I also hit Makefile warnings like “target 'lib-reftable.o' given more
than once,” due to the object being included in both Clar and non-Clar
test builds. And with all declarations in one header, non-Clar tests
see prototypes for Clar-only functions they can't link.
Also facing errors like these:
`LINK t/unit-tests/bin/t-reftable-basics
/usr/bin/ld: t/unit-tests/lib-reftable.o: in function
`cl_reftable_strbuf_writer':
/home/seyik/git/t/unit-tests/lib-reftable.c:113:(.text+0x520):
undefined reference to `clar__assert'`
What would you recommend I do to fix this?
Thanks,
Seyi
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [PATCH v2 10/10] t/unit-tests: adapt lib-reftable{c,h} helper functions to clar
2025-05-26 9:04 ` Seyi Chamber
@ 2025-05-26 12:56 ` Patrick Steinhardt
0 siblings, 0 replies; 31+ messages in thread
From: Patrick Steinhardt @ 2025-05-26 12:56 UTC (permalink / raw)
To: Seyi Chamber; +Cc: git, phillip.wood
On Mon, May 26, 2025 at 10:04:43AM +0100, Seyi Chamber wrote:
> On Mon, 5 May 2025 at 10:52, Patrick Steinhardt <ps@pks.im> wrote:
> >
> > On Mon, May 05, 2025 at 08:27:16AM +0100, Seyi Chamber wrote:
> > > On Fri, 2 May 2025 at 10:58, Patrick Steinhardt <ps@pks.im> wrote:
> > > > On Tue, Apr 29, 2025 at 06:53:02PM +0100, Seyi Kuforiji wrote:
> > > > > diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h
> > > > > index e4c360fa7e..2958db5dc0 100644
> > > > > --- a/t/unit-tests/lib-reftable.h
> > > > > +++ b/t/unit-tests/lib-reftable.h
> > > > > @@ -6,12 +6,12 @@
> > > > >
> > > > > struct reftable_buf;
> > > > >
> > > > > -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > > > > +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id);
> > > > >
> > > > > -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf,
> > > > > +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf,
> > > > > struct reftable_write_options *opts);
> > > > >
> > > > > -void t_reftable_write_to_buf(struct reftable_buf *buf,
> > > > > +void cl_reftable_write_to_buf(struct reftable_buf *buf,
> > > > > struct reftable_ref_record *refs,
> > > > > size_t nrecords,
> > > > > struct reftable_log_record *logs,
> > > >
> > > > It is quite weird that we declare the replacement functions in
> > > > "unit-test.h" in the first commit only to remove them at a later point.
> > > > It would make way more sense if we introduced the functions in
> > > > "t/unit/lib-reftable.{c,h}" right from the start and then only remove
> > > > the unused functions in the last step.
> > > >
> > > > Patrick
> > >
> > > If I get it correctly, you're suggesting I have both the original
> > > functions and the clar-based variant in `t/unit/lib-reftable.{c,h}`
> >
> > Yup, exactly.
> >
> > Patrick
>
> Hi Patrick,
>
> Thanks for the suggestion to move both the original t-helpers and the
> Clar-based cl_ versions into `t/unit/lib-reftable.{c,h}`.
>
> I’ve tried doing that but ran into some build issues. The Clar-based
> functions use clar_assert and clar_assert_equal, which aren’t
> available to non-Clar tests. Since both sets of helpers would live in
> the same object file, this causes linker errors for binaries that
> don’t link against Clar.
>
> I also hit Makefile warnings like “target 'lib-reftable.o' given more
> than once,” due to the object being included in both Clar and non-Clar
> test builds. And with all declarations in one header, non-Clar tests
> see prototypes for Clar-only functions they can't link.
>
> Also facing errors like these:
> `LINK t/unit-tests/bin/t-reftable-basics
> /usr/bin/ld: t/unit-tests/lib-reftable.o: in function
> `cl_reftable_strbuf_writer':
> /home/seyik/git/t/unit-tests/lib-reftable.c:113:(.text+0x520):
> undefined reference to `clar__assert'`
>
> What would you recommend I do to fix this?
Hm. You could also have a separate "lib-reftable-clar.{c,h}" code unit
that contains the new definitions that you introduce early in your
series. At the end of the series you could then delete the old
"lib-reftable.{c,h}" and rename "lib-reftable-clar.{c,h}" accordingly to
have that name. It's requires a bit more shuffling, but given that the
final commit would then be a plain rename it shouldn't be too bad, I
guess.
Patrick
^ permalink raw reply [flat|nested] 31+ messages in thread