reftable: document reading and writing indices
[alt-git.git] / reftable / merged_test.c
blobbf090b474ed5c69b8ef973b86914f36fdd206318
1 /*
2 Copyright 2020 Google LLC
4 Use of this source code is governed by a BSD-style
5 license that can be found in the LICENSE file or at
6 https://developers.google.com/open-source/licenses/bsd
7 */
9 #include "merged.h"
11 #include "system.h"
13 #include "basics.h"
14 #include "blocksource.h"
15 #include "reader.h"
16 #include "record.h"
17 #include "test_framework.h"
18 #include "reftable-merged.h"
19 #include "reftable-tests.h"
20 #include "reftable-generic.h"
21 #include "reftable-writer.h"
23 static void write_test_table(struct strbuf *buf,
24 struct reftable_ref_record refs[], int n)
26 uint64_t min = 0xffffffff;
27 uint64_t max = 0;
28 int i = 0;
29 int err;
31 struct reftable_write_options opts = {
32 .block_size = 256,
34 struct reftable_writer *w = NULL;
35 for (i = 0; i < n; i++) {
36 uint64_t ui = refs[i].update_index;
37 if (ui > max) {
38 max = ui;
40 if (ui < min) {
41 min = ui;
45 w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
46 reftable_writer_set_limits(w, min, max);
48 for (i = 0; i < n; i++) {
49 uint64_t before = refs[i].update_index;
50 int n = reftable_writer_add_ref(w, &refs[i]);
51 EXPECT(n == 0);
52 EXPECT(before == refs[i].update_index);
55 err = reftable_writer_close(w);
56 EXPECT_ERR(err);
58 reftable_writer_free(w);
61 static void write_test_log_table(struct strbuf *buf,
62 struct reftable_log_record logs[], int n,
63 uint64_t update_index)
65 int i = 0;
66 int err;
68 struct reftable_write_options opts = {
69 .block_size = 256,
70 .exact_log_message = 1,
72 struct reftable_writer *w = NULL;
73 w = reftable_new_writer(&strbuf_add_void, &noop_flush, buf, &opts);
74 reftable_writer_set_limits(w, update_index, update_index);
76 for (i = 0; i < n; i++) {
77 int err = reftable_writer_add_log(w, &logs[i]);
78 EXPECT_ERR(err);
81 err = reftable_writer_close(w);
82 EXPECT_ERR(err);
84 reftable_writer_free(w);
87 static struct reftable_merged_table *
88 merged_table_from_records(struct reftable_ref_record **refs,
89 struct reftable_block_source **source,
90 struct reftable_reader ***readers, int *sizes,
91 struct strbuf *buf, int n)
93 int i = 0;
94 struct reftable_merged_table *mt = NULL;
95 int err;
96 struct reftable_table *tabs =
97 reftable_calloc(n * sizeof(struct reftable_table));
98 *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
99 *source = reftable_calloc(n * sizeof(**source));
100 for (i = 0; i < n; i++) {
101 write_test_table(&buf[i], refs[i], sizes[i]);
102 block_source_from_strbuf(&(*source)[i], &buf[i]);
104 err = reftable_new_reader(&(*readers)[i], &(*source)[i],
105 "name");
106 EXPECT_ERR(err);
107 reftable_table_from_reader(&tabs[i], (*readers)[i]);
110 err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
111 EXPECT_ERR(err);
112 return mt;
115 static void readers_destroy(struct reftable_reader **readers, size_t n)
117 int i = 0;
118 for (; i < n; i++)
119 reftable_reader_free(readers[i]);
120 reftable_free(readers);
123 static void test_merged_between(void)
125 struct reftable_ref_record r1[] = { {
126 .refname = "b",
127 .update_index = 1,
128 .value_type = REFTABLE_REF_VAL1,
129 .value.val1 = { 1, 2, 3, 0 },
130 } };
131 struct reftable_ref_record r2[] = { {
132 .refname = "a",
133 .update_index = 2,
134 .value_type = REFTABLE_REF_DELETION,
135 } };
137 struct reftable_ref_record *refs[] = { r1, r2 };
138 int sizes[] = { 1, 1 };
139 struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT };
140 struct reftable_block_source *bs = NULL;
141 struct reftable_reader **readers = NULL;
142 struct reftable_merged_table *mt =
143 merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2);
144 int i;
145 struct reftable_ref_record ref = { NULL };
146 struct reftable_iterator it = { NULL };
147 int err = reftable_merged_table_seek_ref(mt, &it, "a");
148 EXPECT_ERR(err);
150 err = reftable_iterator_next_ref(&it, &ref);
151 EXPECT_ERR(err);
152 EXPECT(ref.update_index == 2);
153 reftable_ref_record_release(&ref);
154 reftable_iterator_destroy(&it);
155 readers_destroy(readers, 2);
156 reftable_merged_table_free(mt);
157 for (i = 0; i < ARRAY_SIZE(bufs); i++) {
158 strbuf_release(&bufs[i]);
160 reftable_free(bs);
163 static void test_merged(void)
165 struct reftable_ref_record r1[] = {
167 .refname = "a",
168 .update_index = 1,
169 .value_type = REFTABLE_REF_VAL1,
170 .value.val1 = { 1 },
173 .refname = "b",
174 .update_index = 1,
175 .value_type = REFTABLE_REF_VAL1,
176 .value.val1 = { 1 },
179 .refname = "c",
180 .update_index = 1,
181 .value_type = REFTABLE_REF_VAL1,
182 .value.val1 = { 1 },
185 struct reftable_ref_record r2[] = { {
186 .refname = "a",
187 .update_index = 2,
188 .value_type = REFTABLE_REF_DELETION,
189 } };
190 struct reftable_ref_record r3[] = {
192 .refname = "c",
193 .update_index = 3,
194 .value_type = REFTABLE_REF_VAL1,
195 .value.val1 = { 2 },
198 .refname = "d",
199 .update_index = 3,
200 .value_type = REFTABLE_REF_VAL1,
201 .value.val1 = { 1 },
205 struct reftable_ref_record *want[] = {
206 &r2[0],
207 &r1[1],
208 &r3[0],
209 &r3[1],
212 struct reftable_ref_record *refs[] = { r1, r2, r3 };
213 int sizes[3] = { 3, 1, 2 };
214 struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
215 struct reftable_block_source *bs = NULL;
216 struct reftable_reader **readers = NULL;
217 struct reftable_merged_table *mt =
218 merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
220 struct reftable_iterator it = { NULL };
221 int err = reftable_merged_table_seek_ref(mt, &it, "a");
222 struct reftable_ref_record *out = NULL;
223 size_t len = 0;
224 size_t cap = 0;
225 int i = 0;
227 EXPECT_ERR(err);
228 EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
229 EXPECT(reftable_merged_table_min_update_index(mt) == 1);
231 while (len < 100) { /* cap loops/recursion. */
232 struct reftable_ref_record ref = { NULL };
233 int err = reftable_iterator_next_ref(&it, &ref);
234 if (err > 0) {
235 break;
237 if (len == cap) {
238 cap = 2 * cap + 1;
239 out = reftable_realloc(
240 out, sizeof(struct reftable_ref_record) * cap);
242 out[len++] = ref;
244 reftable_iterator_destroy(&it);
246 EXPECT(ARRAY_SIZE(want) == len);
247 for (i = 0; i < len; i++) {
248 EXPECT(reftable_ref_record_equal(want[i], &out[i],
249 GIT_SHA1_RAWSZ));
251 for (i = 0; i < len; i++) {
252 reftable_ref_record_release(&out[i]);
254 reftable_free(out);
256 for (i = 0; i < 3; i++) {
257 strbuf_release(&bufs[i]);
259 readers_destroy(readers, 3);
260 reftable_merged_table_free(mt);
261 reftable_free(bs);
264 static struct reftable_merged_table *
265 merged_table_from_log_records(struct reftable_log_record **logs,
266 struct reftable_block_source **source,
267 struct reftable_reader ***readers, int *sizes,
268 struct strbuf *buf, int n)
270 int i = 0;
271 struct reftable_merged_table *mt = NULL;
272 int err;
273 struct reftable_table *tabs =
274 reftable_calloc(n * sizeof(struct reftable_table));
275 *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
276 *source = reftable_calloc(n * sizeof(**source));
277 for (i = 0; i < n; i++) {
278 write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
279 block_source_from_strbuf(&(*source)[i], &buf[i]);
281 err = reftable_new_reader(&(*readers)[i], &(*source)[i],
282 "name");
283 EXPECT_ERR(err);
284 reftable_table_from_reader(&tabs[i], (*readers)[i]);
287 err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
288 EXPECT_ERR(err);
289 return mt;
292 static void test_merged_logs(void)
294 uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
295 uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
296 uint8_t hash3[GIT_SHA1_RAWSZ] = { 3 };
297 struct reftable_log_record r1[] = {
299 .refname = "a",
300 .update_index = 2,
301 .value_type = REFTABLE_LOG_UPDATE,
302 .value.update = {
303 .old_hash = hash2,
304 /* deletion */
305 .name = "jane doe",
306 .email = "jane@invalid",
307 .message = "message2",
311 .refname = "a",
312 .update_index = 1,
313 .value_type = REFTABLE_LOG_UPDATE,
314 .value.update = {
315 .old_hash = hash1,
316 .new_hash = hash2,
317 .name = "jane doe",
318 .email = "jane@invalid",
319 .message = "message1",
323 struct reftable_log_record r2[] = {
325 .refname = "a",
326 .update_index = 3,
327 .value_type = REFTABLE_LOG_UPDATE,
328 .value.update = {
329 .new_hash = hash3,
330 .name = "jane doe",
331 .email = "jane@invalid",
332 .message = "message3",
336 struct reftable_log_record r3[] = {
338 .refname = "a",
339 .update_index = 2,
340 .value_type = REFTABLE_LOG_DELETION,
343 struct reftable_log_record *want[] = {
344 &r2[0],
345 &r3[0],
346 &r1[1],
349 struct reftable_log_record *logs[] = { r1, r2, r3 };
350 int sizes[3] = { 2, 1, 1 };
351 struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
352 struct reftable_block_source *bs = NULL;
353 struct reftable_reader **readers = NULL;
354 struct reftable_merged_table *mt = merged_table_from_log_records(
355 logs, &bs, &readers, sizes, bufs, 3);
357 struct reftable_iterator it = { NULL };
358 int err = reftable_merged_table_seek_log(mt, &it, "a");
359 struct reftable_log_record *out = NULL;
360 size_t len = 0;
361 size_t cap = 0;
362 int i = 0;
364 EXPECT_ERR(err);
365 EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
366 EXPECT(reftable_merged_table_min_update_index(mt) == 1);
368 while (len < 100) { /* cap loops/recursion. */
369 struct reftable_log_record log = { NULL };
370 int err = reftable_iterator_next_log(&it, &log);
371 if (err > 0) {
372 break;
374 if (len == cap) {
375 cap = 2 * cap + 1;
376 out = reftable_realloc(
377 out, sizeof(struct reftable_log_record) * cap);
379 out[len++] = log;
381 reftable_iterator_destroy(&it);
383 EXPECT(ARRAY_SIZE(want) == len);
384 for (i = 0; i < len; i++) {
385 EXPECT(reftable_log_record_equal(want[i], &out[i],
386 GIT_SHA1_RAWSZ));
389 err = reftable_merged_table_seek_log_at(mt, &it, "a", 2);
390 EXPECT_ERR(err);
391 reftable_log_record_release(&out[0]);
392 err = reftable_iterator_next_log(&it, &out[0]);
393 EXPECT_ERR(err);
394 EXPECT(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ));
395 reftable_iterator_destroy(&it);
397 for (i = 0; i < len; i++) {
398 reftable_log_record_release(&out[i]);
400 reftable_free(out);
402 for (i = 0; i < 3; i++) {
403 strbuf_release(&bufs[i]);
405 readers_destroy(readers, 3);
406 reftable_merged_table_free(mt);
407 reftable_free(bs);
410 static void test_default_write_opts(void)
412 struct reftable_write_options opts = { 0 };
413 struct strbuf buf = STRBUF_INIT;
414 struct reftable_writer *w =
415 reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
417 struct reftable_ref_record rec = {
418 .refname = "master",
419 .update_index = 1,
421 int err;
422 struct reftable_block_source source = { NULL };
423 struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1);
424 uint32_t hash_id;
425 struct reftable_reader *rd = NULL;
426 struct reftable_merged_table *merged = NULL;
428 reftable_writer_set_limits(w, 1, 1);
430 err = reftable_writer_add_ref(w, &rec);
431 EXPECT_ERR(err);
433 err = reftable_writer_close(w);
434 EXPECT_ERR(err);
435 reftable_writer_free(w);
437 block_source_from_strbuf(&source, &buf);
439 err = reftable_new_reader(&rd, &source, "filename");
440 EXPECT_ERR(err);
442 hash_id = reftable_reader_hash_id(rd);
443 EXPECT(hash_id == GIT_SHA1_FORMAT_ID);
445 reftable_table_from_reader(&tab[0], rd);
446 err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID);
447 EXPECT_ERR(err);
449 reftable_reader_free(rd);
450 reftable_merged_table_free(merged);
451 strbuf_release(&buf);
454 /* XXX test refs_for(oid) */
456 int merged_test_main(int argc, const char *argv[])
458 RUN_TEST(test_merged_logs);
459 RUN_TEST(test_merged_between);
460 RUN_TEST(test_merged);
461 RUN_TEST(test_default_write_opts);
462 return 0;