From f1b60b7c666416b00f96e3f58552bfe52ca73130 Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:42 +0530 Subject: [PATCH 1/7] reftable: remove unnecessary curly braces in reftable/pq.c According to Documentation/CodingGuidelines, control-flow statements with a single line as their body must omit curly braces. Make reftable/pq.c conform to this guideline. Besides that, remove unnecessary newlines and variable assignment. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- reftable/pq.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/reftable/pq.c b/reftable/pq.c index 7fb45d8c60..1a180c5fa6 100644 --- a/reftable/pq.c +++ b/reftable/pq.c @@ -27,22 +27,16 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq) pq->heap[0] = pq->heap[pq->len - 1]; pq->len--; - i = 0; while (i < pq->len) { int min = i; int j = 2 * i + 1; int k = 2 * i + 2; - if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) { + if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) min = j; - } - if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) { + if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) min = k; - } - - if (min == i) { + if (min == i) break; - } - SWAP(pq->heap[i], pq->heap[min]); i = min; } @@ -60,12 +54,9 @@ void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry i = pq->len - 1; while (i > 0) { int j = (i - 1) / 2; - if (pq_less(&pq->heap[j], &pq->heap[i])) { + if (pq_less(&pq->heap[j], &pq->heap[i])) break; - } - SWAP(pq->heap[j], pq->heap[i]); - i = j; } } From 2a85906348d324056f8d147a51156d9901a9675f Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:43 +0530 Subject: [PATCH 2/7] reftable: change the type of array indices to 'size_t' in reftable/pq.c The variables 'i', 'j', 'k' and 'min' are used as indices for 'pq->heap', which is an array. Additionally, 'pq->len' is of type 'size_t' and is often used to assign values to these variables. Hence, change the type of these variables from 'int' to 'size_t'. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- reftable/pq.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reftable/pq.c b/reftable/pq.c index 1a180c5fa6..2b5b7d1c0e 100644 --- a/reftable/pq.c +++ b/reftable/pq.c @@ -22,15 +22,15 @@ int pq_less(struct pq_entry *a, struct pq_entry *b) struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq) { - int i = 0; + size_t i = 0; struct pq_entry e = pq->heap[0]; pq->heap[0] = pq->heap[pq->len - 1]; pq->len--; while (i < pq->len) { - int min = i; - int j = 2 * i + 1; - int k = 2 * i + 2; + size_t min = i; + size_t j = 2 * i + 1; + size_t k = 2 * i + 2; if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) min = j; if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) @@ -46,14 +46,14 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq) void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e) { - int i = 0; + size_t i = 0; REFTABLE_ALLOC_GROW(pq->heap, pq->len + 1, pq->cap); pq->heap[pq->len++] = *e; i = pq->len - 1; while (i > 0) { - int j = (i - 1) / 2; + size_t j = (i - 1) / 2; if (pq_less(&pq->heap[j], &pq->heap[i])) break; SWAP(pq->heap[j], pq->heap[i]); From a08ea27cd0efcaa4268f29026edfa162b14c73ad Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:44 +0530 Subject: [PATCH 3/7] t: move reftable/pq_test.c to the unit testing framework reftable/pq_test.c exercises a priority queue defined by reftable/pq.{c, h}. Migrate reftable/pq_test.c to the unit testing framework. Migration involves refactoring the tests to use the unit testing framework instead of reftable's test framework, and renaming the tests to align with unit-tests' standards. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- Makefile | 2 +- reftable/reftable-tests.h | 1 - t/helper/test-reftable.c | 1 - .../pq_test.c => t/unit-tests/t-reftable-pq.c | 39 ++++++++----------- 4 files changed, 17 insertions(+), 26 deletions(-) rename reftable/pq_test.c => t/unit-tests/t-reftable-pq.c (61%) diff --git a/Makefile b/Makefile index d6479092a0..1ee83e98dc 100644 --- a/Makefile +++ b/Makefile @@ -1340,6 +1340,7 @@ UNIT_TEST_PROGRAMS += t-oidmap UNIT_TEST_PROGRAMS += t-oidtree UNIT_TEST_PROGRAMS += t-prio-queue UNIT_TEST_PROGRAMS += t-reftable-basics +UNIT_TEST_PROGRAMS += t-reftable-pq UNIT_TEST_PROGRAMS += t-reftable-record UNIT_TEST_PROGRAMS += t-strbuf UNIT_TEST_PROGRAMS += t-strcmp-offset @@ -2681,7 +2682,6 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_TEST_OBJS += reftable/block_test.o REFTABLE_TEST_OBJS += reftable/dump.o REFTABLE_TEST_OBJS += reftable/merged_test.o -REFTABLE_TEST_OBJS += reftable/pq_test.o REFTABLE_TEST_OBJS += reftable/readwrite_test.o REFTABLE_TEST_OBJS += reftable/stack_test.o REFTABLE_TEST_OBJS += reftable/test_framework.o diff --git a/reftable/reftable-tests.h b/reftable/reftable-tests.h index 114cc3d053..67283faf06 100644 --- a/reftable/reftable-tests.h +++ b/reftable/reftable-tests.h @@ -12,7 +12,6 @@ https://developers.google.com/open-source/licenses/bsd int basics_test_main(int argc, const char **argv); int block_test_main(int argc, const char **argv); int merged_test_main(int argc, const char **argv); -int pq_test_main(int argc, const char **argv); int record_test_main(int argc, const char **argv); int readwrite_test_main(int argc, const char **argv); int stack_test_main(int argc, const char **argv); diff --git a/t/helper/test-reftable.c b/t/helper/test-reftable.c index aa6538a8da..b808ad3e12 100644 --- a/t/helper/test-reftable.c +++ b/t/helper/test-reftable.c @@ -7,7 +7,6 @@ int cmd__reftable(int argc, const char **argv) /* test from simple to complex. */ block_test_main(argc, argv); tree_test_main(argc, argv); - pq_test_main(argc, argv); readwrite_test_main(argc, argv); merged_test_main(argc, argv); stack_test_main(argc, argv); diff --git a/reftable/pq_test.c b/t/unit-tests/t-reftable-pq.c similarity index 61% rename from reftable/pq_test.c rename to t/unit-tests/t-reftable-pq.c index b7d3c80cc7..a78aba9e71 100644 --- a/reftable/pq_test.c +++ b/t/unit-tests/t-reftable-pq.c @@ -6,35 +6,28 @@ license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd */ -#include "system.h" - -#include "basics.h" -#include "constants.h" -#include "pq.h" -#include "record.h" -#include "reftable-tests.h" -#include "test_framework.h" +#include "test-lib.h" +#include "reftable/constants.h" +#include "reftable/pq.h" void merged_iter_pqueue_check(struct merged_iter_pqueue pq) { - int i; - for (i = 1; i < pq.len; i++) { - int parent = (i - 1) / 2; - - EXPECT(pq_less(&pq.heap[parent], &pq.heap[i])); + 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 void test_pq(void) +static void t_pq(void) { - struct merged_iter_pqueue pq = { NULL }; + struct merged_iter_pqueue pq = { 0 }; struct reftable_record recs[54]; - int N = ARRAY_SIZE(recs) - 1, i; + size_t N = ARRAY_SIZE(recs) - 1, i; char *last = NULL; for (i = 0; i < N; i++) { struct strbuf refname = STRBUF_INIT; - strbuf_addf(&refname, "%02d", i); + strbuf_addf(&refname, "%02"PRIuMAX, (uintmax_t)i); reftable_record_init(&recs[i], BLOCK_TYPE_REF); recs[i].u.ref.refname = strbuf_detach(&refname, NULL); @@ -48,7 +41,6 @@ static void test_pq(void) merged_iter_pqueue_add(&pq, &e); merged_iter_pqueue_check(pq); - i = (i * 7) % N; } while (i != 1); @@ -56,9 +48,9 @@ static void test_pq(void) struct pq_entry e = merged_iter_pqueue_remove(&pq); merged_iter_pqueue_check(pq); - EXPECT(reftable_record_type(e.rec) == BLOCK_TYPE_REF); + check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); if (last) - EXPECT(strcmp(last, e.rec->u.ref.refname) < 0); + check_int(strcmp(last, e.rec->u.ref.refname), <, 0); last = e.rec->u.ref.refname; } @@ -67,8 +59,9 @@ static void test_pq(void) merged_iter_pqueue_release(&pq); } -int pq_test_main(int argc, const char *argv[]) +int cmd_main(int argc, const char *argv[]) { - RUN_TEST(test_pq); - return 0; + TEST(t_pq(), "pq works"); + + return test_done(); } From 2e707447e127c9da3736fe7fa4943640078cc97d Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:45 +0530 Subject: [PATCH 4/7] t-reftable-pq: make merged_iter_pqueue_check() static merged_iter_pqueue_check() is a function previously defined in reftable/pq_test.c (now t/unit-tests/t-reftable-pq.c) and used in the testing of a priority queue as defined by reftable/pq.{c, h}. As such, this function is only called by reftable/pq_test.c and it makes little sense to expose it to non-testing code via reftable/pq.h. Hence, make this function static and remove its prototype from reftable/pq.h. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- reftable/pq.h | 1 - t/unit-tests/t-reftable-pq.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/reftable/pq.h b/reftable/pq.h index f796c23179..707bd26767 100644 --- a/reftable/pq.h +++ b/reftable/pq.h @@ -22,7 +22,6 @@ struct merged_iter_pqueue { size_t cap; }; -void merged_iter_pqueue_check(struct merged_iter_pqueue pq); struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq); void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e); void merged_iter_pqueue_release(struct merged_iter_pqueue *pq); diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c index a78aba9e71..220e82be19 100644 --- a/t/unit-tests/t-reftable-pq.c +++ b/t/unit-tests/t-reftable-pq.c @@ -10,7 +10,7 @@ https://developers.google.com/open-source/licenses/bsd #include "reftable/constants.h" #include "reftable/pq.h" -void merged_iter_pqueue_check(struct merged_iter_pqueue pq) +static void merged_iter_pqueue_check(struct merged_iter_pqueue pq) { for (size_t i = 1; i < pq.len; i++) { size_t parent = (i - 1) / 2; From b37b71b129d9536aca7bb242ef9d0ab43243f613 Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:46 +0530 Subject: [PATCH 5/7] t-reftable-pq: make merged_iter_pqueue_check() callable by reference merged_iter_pqueue_check() checks the validity of a priority queue represented by a merged_iter_pqueue struct by asserting the parent-child relation in the struct's heap. Explicity passing a struct to this function means a copy of the entire struct is created, which is inefficient. Make the function accept a pointer to the struct instead. This is safe to do since the function doesn't modify the struct in any way. Make the function parameter 'const' to assert immutability. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- t/unit-tests/t-reftable-pq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c index 220e82be19..9230dd9b9e 100644 --- a/t/unit-tests/t-reftable-pq.c +++ b/t/unit-tests/t-reftable-pq.c @@ -10,11 +10,11 @@ https://developers.google.com/open-source/licenses/bsd #include "reftable/constants.h" #include "reftable/pq.h" -static void merged_iter_pqueue_check(struct merged_iter_pqueue pq) +static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq) { - for (size_t i = 1; i < pq.len; i++) { + for (size_t i = 1; i < pq->len; i++) { size_t parent = (i - 1) / 2; - check(pq_less(&pq.heap[parent], &pq.heap[i])); + check(pq_less(&pq->heap[parent], &pq->heap[i])); } } @@ -40,13 +40,13 @@ static void t_pq(void) }; merged_iter_pqueue_add(&pq, &e); - merged_iter_pqueue_check(pq); + merged_iter_pqueue_check(&pq); i = (i * 7) % N; } while (i != 1); while (!merged_iter_pqueue_is_empty(pq)) { struct pq_entry e = merged_iter_pqueue_remove(&pq); - merged_iter_pqueue_check(pq); + merged_iter_pqueue_check(&pq); check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); if (last) From c2f831cdfcc41e257380e3b3ca13423ada20fd59 Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:47 +0530 Subject: [PATCH 6/7] t-reftable-pq: add test for index based comparison When comparing two entries, the priority queue as defined by reftable/pq.{c, h} first compares the entries on the basis of their ref-record's keys. If the keys turn out to be equal, the comparison is then made on the basis of their update indices (which are never equal). In the current testing setup, only the case for comparison on the basis of ref-record's keys is exercised. Add a test for index-based comparison as well. Rename the existing test to reflect its nature of only testing record-based comparison. While at it, replace 'strbuf_detach' with 'xstrfmt' to assign refnames in the existing test. This makes the test conciser. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- t/unit-tests/t-reftable-pq.c | 48 +++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c index 9230dd9b9e..70ff89507d 100644 --- a/t/unit-tests/t-reftable-pq.c +++ b/t/unit-tests/t-reftable-pq.c @@ -18,7 +18,7 @@ static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq) } } -static void t_pq(void) +static void t_pq_record(void) { struct merged_iter_pqueue pq = { 0 }; struct reftable_record recs[54]; @@ -26,11 +26,8 @@ static void t_pq(void) char *last = NULL; for (i = 0; i < N; i++) { - struct strbuf refname = STRBUF_INIT; - strbuf_addf(&refname, "%02"PRIuMAX, (uintmax_t)i); - reftable_record_init(&recs[i], BLOCK_TYPE_REF); - recs[i].u.ref.refname = strbuf_detach(&refname, NULL); + recs[i].u.ref.refname = xstrfmt("%02"PRIuMAX, (uintmax_t)i); } i = 1; @@ -59,9 +56,48 @@ static void t_pq(void) merged_iter_pqueue_release(&pq); } +static void t_pq_index(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[13]; + char *last = NULL; + size_t N = ARRAY_SIZE(recs), i; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = (char *) "refs/heads/master"; + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + .index = i, + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + for (i = N - 1; i > 0; i--) { + struct pq_entry e = merged_iter_pqueue_remove(&pq); + merged_iter_pqueue_check(&pq); + + 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); +} + int cmd_main(int argc, const char *argv[]) { - TEST(t_pq(), "pq works"); + TEST(t_pq_record(), "pq works with record-based comparison"); + TEST(t_pq_index(), "pq works with index-based comparison"); return test_done(); } From 0dc84a806c53b02b465616d26efbca73682c58dd Mon Sep 17 00:00:00 2001 From: Chandra Pratap Date: Thu, 1 Aug 2024 16:29:48 +0530 Subject: [PATCH 7/7] t-reftable-pq: add tests for merged_iter_pqueue_top() merged_iter_pqueue_top() as defined by reftable/pq.{c, h} returns the element at the top of a priority-queue's heap without removing it. Since there are no tests for this function in the existing setup, add tests for the same. Mentored-by: Patrick Steinhardt Mentored-by: Christian Couder Signed-off-by: Chandra Pratap Signed-off-by: Junio C Hamano --- t/unit-tests/t-reftable-pq.c | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c index 70ff89507d..039bd0f1f9 100644 --- a/t/unit-tests/t-reftable-pq.c +++ b/t/unit-tests/t-reftable-pq.c @@ -18,6 +18,11 @@ static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq) } } +static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b) +{ + return !reftable_record_cmp(a->rec, b->rec) && (a->index == b->index); +} + static void t_pq_record(void) { struct merged_iter_pqueue pq = { 0 }; @@ -42,9 +47,11 @@ static void t_pq_record(void) } while (i != 1); while (!merged_iter_pqueue_is_empty(pq)) { + struct pq_entry top = merged_iter_pqueue_top(pq); struct pq_entry e = merged_iter_pqueue_remove(&pq); merged_iter_pqueue_check(&pq); + check(pq_entry_equal(&top, &e)); check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); if (last) check_int(strcmp(last, e.rec->u.ref.refname), <, 0); @@ -81,9 +88,11 @@ static void t_pq_index(void) } while (i != 1); for (i = N - 1; i > 0; i--) { + struct pq_entry top = merged_iter_pqueue_top(pq); struct pq_entry e = merged_iter_pqueue_remove(&pq); merged_iter_pqueue_check(&pq); + check(pq_entry_equal(&top, &e)); check(reftable_record_type(e.rec) == BLOCK_TYPE_REF); check_int(e.index, ==, i); if (last) @@ -94,10 +103,50 @@ static void t_pq_index(void) merged_iter_pqueue_release(&pq); } +static void t_merged_iter_pqueue_top(void) +{ + struct merged_iter_pqueue pq = { 0 }; + struct reftable_record recs[13]; + size_t N = ARRAY_SIZE(recs), i; + + for (i = 0; i < N; i++) { + reftable_record_init(&recs[i], BLOCK_TYPE_REF); + recs[i].u.ref.refname = (char *) "refs/heads/master"; + } + + i = 1; + do { + struct pq_entry e = { + .rec = &recs[i], + .index = i, + }; + + merged_iter_pqueue_add(&pq, &e); + merged_iter_pqueue_check(&pq); + i = (i * 7) % N; + } while (i != 1); + + for (i = N - 1; i > 0; i--) { + struct pq_entry top = merged_iter_pqueue_top(pq); + struct pq_entry e = merged_iter_pqueue_remove(&pq); + + merged_iter_pqueue_check(&pq); + check(pq_entry_equal(&top, &e)); + check(reftable_record_equal(top.rec, &recs[i], GIT_SHA1_RAWSZ)); + for (size_t j = 0; i < pq.len; j++) { + check(pq_less(&top, &pq.heap[j])); + check_int(top.index, >, j); + } + } + + merged_iter_pqueue_release(&pq); +} + int cmd_main(int argc, const char *argv[]) { TEST(t_pq_record(), "pq works with record-based comparison"); TEST(t_pq_index(), "pq works with index-based comparison"); + TEST(t_merged_iter_pqueue_top(), "merged_iter_pqueue_top works"); return test_done(); }