replace strbuf_expand() with strbuf_expand_step()
[git.git] / reftable / merged_test.c
blobd08c16abefbc3d8562c29cf1703d1a6908f71dcd
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 "constants.h"
16 #include "reader.h"
17 #include "record.h"
18 #include "test_framework.h"
19 #include "reftable-merged.h"
20 #include "reftable-tests.h"
21 #include "reftable-generic.h"
22 #include "reftable-writer.h"
24 static void write_test_table(struct strbuf *buf,
25 struct reftable_ref_record refs[], int n)
27 uint64_t min = 0xffffffff;
28 uint64_t max = 0;
29 int i = 0;
30 int err;
32 struct reftable_write_options opts = {
33 .block_size = 256,
35 struct reftable_writer *w = NULL;
36 for (i = 0; i < n; i++) {
37 uint64_t ui = refs[i].update_index;
38 if (ui > max) {
39 max = ui;
41 if (ui < min) {
42 min = ui;
46 w = reftable_new_writer(&strbuf_add_void, buf, &opts);
47 reftable_writer_set_limits(w, min, max);
49 for (i = 0; i < n; i++) {
50 uint64_t before = refs[i].update_index;
51 int n = reftable_writer_add_ref(w, &refs[i]);
52 EXPECT(n == 0);
53 EXPECT(before == refs[i].update_index);
56 err = reftable_writer_close(w);
57 EXPECT_ERR(err);
59 reftable_writer_free(w);
62 static void write_test_log_table(struct strbuf *buf,
63 struct reftable_log_record logs[], int n,
64 uint64_t update_index)
66 int i = 0;
67 int err;
69 struct reftable_write_options opts = {
70 .block_size = 256,
71 .exact_log_message = 1,
73 struct reftable_writer *w = NULL;
74 w = reftable_new_writer(&strbuf_add_void, buf, &opts);
75 reftable_writer_set_limits(w, update_index, update_index);
77 for (i = 0; i < n; i++) {
78 int err = reftable_writer_add_log(w, &logs[i]);
79 EXPECT_ERR(err);
82 err = reftable_writer_close(w);
83 EXPECT_ERR(err);
85 reftable_writer_free(w);
88 static struct reftable_merged_table *
89 merged_table_from_records(struct reftable_ref_record **refs,
90 struct reftable_block_source **source,
91 struct reftable_reader ***readers, int *sizes,
92 struct strbuf *buf, int n)
94 int i = 0;
95 struct reftable_merged_table *mt = NULL;
96 int err;
97 struct reftable_table *tabs =
98 reftable_calloc(n * sizeof(struct reftable_table));
99 *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
100 *source = reftable_calloc(n * sizeof(**source));
101 for (i = 0; i < n; i++) {
102 write_test_table(&buf[i], refs[i], sizes[i]);
103 block_source_from_strbuf(&(*source)[i], &buf[i]);
105 err = reftable_new_reader(&(*readers)[i], &(*source)[i],
106 "name");
107 EXPECT_ERR(err);
108 reftable_table_from_reader(&tabs[i], (*readers)[i]);
111 err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
112 EXPECT_ERR(err);
113 return mt;
116 static void readers_destroy(struct reftable_reader **readers, size_t n)
118 int i = 0;
119 for (; i < n; i++)
120 reftable_reader_free(readers[i]);
121 reftable_free(readers);
124 static void test_merged_between(void)
126 uint8_t hash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 0 };
128 struct reftable_ref_record r1[] = { {
129 .refname = "b",
130 .update_index = 1,
131 .value_type = REFTABLE_REF_VAL1,
132 .value.val1 = hash1,
133 } };
134 struct reftable_ref_record r2[] = { {
135 .refname = "a",
136 .update_index = 2,
137 .value_type = REFTABLE_REF_DELETION,
138 } };
140 struct reftable_ref_record *refs[] = { r1, r2 };
141 int sizes[] = { 1, 1 };
142 struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT };
143 struct reftable_block_source *bs = NULL;
144 struct reftable_reader **readers = NULL;
145 struct reftable_merged_table *mt =
146 merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2);
147 int i;
148 struct reftable_ref_record ref = { NULL };
149 struct reftable_iterator it = { NULL };
150 int err = reftable_merged_table_seek_ref(mt, &it, "a");
151 EXPECT_ERR(err);
153 err = reftable_iterator_next_ref(&it, &ref);
154 EXPECT_ERR(err);
155 EXPECT(ref.update_index == 2);
156 reftable_ref_record_release(&ref);
157 reftable_iterator_destroy(&it);
158 readers_destroy(readers, 2);
159 reftable_merged_table_free(mt);
160 for (i = 0; i < ARRAY_SIZE(bufs); i++) {
161 strbuf_release(&bufs[i]);
163 reftable_free(bs);
166 static void test_merged(void)
168 uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
169 uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
170 struct reftable_ref_record r1[] = {
172 .refname = "a",
173 .update_index = 1,
174 .value_type = REFTABLE_REF_VAL1,
175 .value.val1 = hash1,
178 .refname = "b",
179 .update_index = 1,
180 .value_type = REFTABLE_REF_VAL1,
181 .value.val1 = hash1,
184 .refname = "c",
185 .update_index = 1,
186 .value_type = REFTABLE_REF_VAL1,
187 .value.val1 = hash1,
190 struct reftable_ref_record r2[] = { {
191 .refname = "a",
192 .update_index = 2,
193 .value_type = REFTABLE_REF_DELETION,
194 } };
195 struct reftable_ref_record r3[] = {
197 .refname = "c",
198 .update_index = 3,
199 .value_type = REFTABLE_REF_VAL1,
200 .value.val1 = hash2,
203 .refname = "d",
204 .update_index = 3,
205 .value_type = REFTABLE_REF_VAL1,
206 .value.val1 = hash1,
210 struct reftable_ref_record *want[] = {
211 &r2[0],
212 &r1[1],
213 &r3[0],
214 &r3[1],
217 struct reftable_ref_record *refs[] = { r1, r2, r3 };
218 int sizes[3] = { 3, 1, 2 };
219 struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
220 struct reftable_block_source *bs = NULL;
221 struct reftable_reader **readers = NULL;
222 struct reftable_merged_table *mt =
223 merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
225 struct reftable_iterator it = { NULL };
226 int err = reftable_merged_table_seek_ref(mt, &it, "a");
227 struct reftable_ref_record *out = NULL;
228 size_t len = 0;
229 size_t cap = 0;
230 int i = 0;
232 EXPECT_ERR(err);
233 EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
234 EXPECT(reftable_merged_table_min_update_index(mt) == 1);
236 while (len < 100) { /* cap loops/recursion. */
237 struct reftable_ref_record ref = { NULL };
238 int err = reftable_iterator_next_ref(&it, &ref);
239 if (err > 0) {
240 break;
242 if (len == cap) {
243 cap = 2 * cap + 1;
244 out = reftable_realloc(
245 out, sizeof(struct reftable_ref_record) * cap);
247 out[len++] = ref;
249 reftable_iterator_destroy(&it);
251 EXPECT(ARRAY_SIZE(want) == len);
252 for (i = 0; i < len; i++) {
253 EXPECT(reftable_ref_record_equal(want[i], &out[i],
254 GIT_SHA1_RAWSZ));
256 for (i = 0; i < len; i++) {
257 reftable_ref_record_release(&out[i]);
259 reftable_free(out);
261 for (i = 0; i < 3; i++) {
262 strbuf_release(&bufs[i]);
264 readers_destroy(readers, 3);
265 reftable_merged_table_free(mt);
266 reftable_free(bs);
269 static struct reftable_merged_table *
270 merged_table_from_log_records(struct reftable_log_record **logs,
271 struct reftable_block_source **source,
272 struct reftable_reader ***readers, int *sizes,
273 struct strbuf *buf, int n)
275 int i = 0;
276 struct reftable_merged_table *mt = NULL;
277 int err;
278 struct reftable_table *tabs =
279 reftable_calloc(n * sizeof(struct reftable_table));
280 *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
281 *source = reftable_calloc(n * sizeof(**source));
282 for (i = 0; i < n; i++) {
283 write_test_log_table(&buf[i], logs[i], sizes[i], i + 1);
284 block_source_from_strbuf(&(*source)[i], &buf[i]);
286 err = reftable_new_reader(&(*readers)[i], &(*source)[i],
287 "name");
288 EXPECT_ERR(err);
289 reftable_table_from_reader(&tabs[i], (*readers)[i]);
292 err = reftable_new_merged_table(&mt, tabs, n, GIT_SHA1_FORMAT_ID);
293 EXPECT_ERR(err);
294 return mt;
297 static void test_merged_logs(void)
299 uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
300 uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
301 uint8_t hash3[GIT_SHA1_RAWSZ] = { 3 };
302 struct reftable_log_record r1[] = {
304 .refname = "a",
305 .update_index = 2,
306 .value_type = REFTABLE_LOG_UPDATE,
307 .value.update = {
308 .old_hash = hash2,
309 /* deletion */
310 .name = "jane doe",
311 .email = "jane@invalid",
312 .message = "message2",
316 .refname = "a",
317 .update_index = 1,
318 .value_type = REFTABLE_LOG_UPDATE,
319 .value.update = {
320 .old_hash = hash1,
321 .new_hash = hash2,
322 .name = "jane doe",
323 .email = "jane@invalid",
324 .message = "message1",
328 struct reftable_log_record r2[] = {
330 .refname = "a",
331 .update_index = 3,
332 .value_type = REFTABLE_LOG_UPDATE,
333 .value.update = {
334 .new_hash = hash3,
335 .name = "jane doe",
336 .email = "jane@invalid",
337 .message = "message3",
341 struct reftable_log_record r3[] = {
343 .refname = "a",
344 .update_index = 2,
345 .value_type = REFTABLE_LOG_DELETION,
348 struct reftable_log_record *want[] = {
349 &r2[0],
350 &r3[0],
351 &r1[1],
354 struct reftable_log_record *logs[] = { r1, r2, r3 };
355 int sizes[3] = { 2, 1, 1 };
356 struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
357 struct reftable_block_source *bs = NULL;
358 struct reftable_reader **readers = NULL;
359 struct reftable_merged_table *mt = merged_table_from_log_records(
360 logs, &bs, &readers, sizes, bufs, 3);
362 struct reftable_iterator it = { NULL };
363 int err = reftable_merged_table_seek_log(mt, &it, "a");
364 struct reftable_log_record *out = NULL;
365 size_t len = 0;
366 size_t cap = 0;
367 int i = 0;
369 EXPECT_ERR(err);
370 EXPECT(reftable_merged_table_hash_id(mt) == GIT_SHA1_FORMAT_ID);
371 EXPECT(reftable_merged_table_min_update_index(mt) == 1);
373 while (len < 100) { /* cap loops/recursion. */
374 struct reftable_log_record log = { NULL };
375 int err = reftable_iterator_next_log(&it, &log);
376 if (err > 0) {
377 break;
379 if (len == cap) {
380 cap = 2 * cap + 1;
381 out = reftable_realloc(
382 out, sizeof(struct reftable_log_record) * cap);
384 out[len++] = log;
386 reftable_iterator_destroy(&it);
388 EXPECT(ARRAY_SIZE(want) == len);
389 for (i = 0; i < len; i++) {
390 EXPECT(reftable_log_record_equal(want[i], &out[i],
391 GIT_SHA1_RAWSZ));
394 err = reftable_merged_table_seek_log_at(mt, &it, "a", 2);
395 EXPECT_ERR(err);
396 reftable_log_record_release(&out[0]);
397 err = reftable_iterator_next_log(&it, &out[0]);
398 EXPECT_ERR(err);
399 EXPECT(reftable_log_record_equal(&out[0], &r3[0], GIT_SHA1_RAWSZ));
400 reftable_iterator_destroy(&it);
402 for (i = 0; i < len; i++) {
403 reftable_log_record_release(&out[i]);
405 reftable_free(out);
407 for (i = 0; i < 3; i++) {
408 strbuf_release(&bufs[i]);
410 readers_destroy(readers, 3);
411 reftable_merged_table_free(mt);
412 reftable_free(bs);
415 static void test_default_write_opts(void)
417 struct reftable_write_options opts = { 0 };
418 struct strbuf buf = STRBUF_INIT;
419 struct reftable_writer *w =
420 reftable_new_writer(&strbuf_add_void, &buf, &opts);
422 struct reftable_ref_record rec = {
423 .refname = "master",
424 .update_index = 1,
426 int err;
427 struct reftable_block_source source = { NULL };
428 struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1);
429 uint32_t hash_id;
430 struct reftable_reader *rd = NULL;
431 struct reftable_merged_table *merged = NULL;
433 reftable_writer_set_limits(w, 1, 1);
435 err = reftable_writer_add_ref(w, &rec);
436 EXPECT_ERR(err);
438 err = reftable_writer_close(w);
439 EXPECT_ERR(err);
440 reftable_writer_free(w);
442 block_source_from_strbuf(&source, &buf);
444 err = reftable_new_reader(&rd, &source, "filename");
445 EXPECT_ERR(err);
447 hash_id = reftable_reader_hash_id(rd);
448 EXPECT(hash_id == GIT_SHA1_FORMAT_ID);
450 reftable_table_from_reader(&tab[0], rd);
451 err = reftable_new_merged_table(&merged, tab, 1, GIT_SHA1_FORMAT_ID);
452 EXPECT_ERR(err);
454 reftable_reader_free(rd);
455 reftable_merged_table_free(merged);
456 strbuf_release(&buf);
459 /* XXX test refs_for(oid) */
461 int merged_test_main(int argc, const char *argv[])
463 RUN_TEST(test_merged_logs);
464 RUN_TEST(test_merged_between);
465 RUN_TEST(test_merged);
466 RUN_TEST(test_default_write_opts);
467 return 0;